diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 7d4783de..5ec9d517 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -24,7 +24,7 @@ jobs:
- name: e2e-mongo-test-clean
run: cd tdrive && docker-compose -f docker-compose.tests.yml stop
- name: e2e-opensearch-test
- run: cd tdrive && docker-compose -f docker-compose.dev.tests.opensearch.yml run -e NODE_OPTIONS=--unhandled-rejections=warn -e SEARCH_DRIVER=opensearch -e DB_DRIVER=mongodb -e PUBSUB_TYPE=local node npm run test:all
+ run: cd tdrive && docker-compose -f docker-compose.dev.tests.opensearch.yml run -e NODE_OPTIONS=--unhandled-rejections=warn -e SEARCH_DRIVER=opensearch -e DB_DRIVER=postgres -e PUBSUB_TYPE=local node npm run test:all
- name: e2e-opensearch-test-clean
run: cd tdrive && docker-compose -f docker-compose.dev.tests.opensearch.yml stop
- name: e2e-cassandra-test
diff --git a/.run/Run all e2e [MongoDB].run.xml b/.run/Run all e2e [MongoDB].run.xml
index c0ac2a18..359d59bc 100644
--- a/.run/Run all e2e [MongoDB].run.xml
+++ b/.run/Run all e2e [MongoDB].run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Run all e2e [OpenSearch].run.xml b/.run/Run all e2e [OpenSearch].run.xml
new file mode 100644
index 00000000..1e1a1174
--- /dev/null
+++ b/.run/Run all e2e [OpenSearch].run.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/TDrive[Postgres] local.run.xml b/.run/TDrive[Postgres] local.run.xml
new file mode 100644
index 00000000..bc30882b
--- /dev/null
+++ b/.run/TDrive[Postgres] local.run.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tdrive/backend/node/config/custom-environment-variables.json b/tdrive/backend/node/config/custom-environment-variables.json
index 49d3e308..d2f35c10 100644
--- a/tdrive/backend/node/config/custom-environment-variables.json
+++ b/tdrive/backend/node/config/custom-environment-variables.json
@@ -53,6 +53,14 @@
"contactPoints": "DB_CASSANDRA_URI",
"localDataCenter": "DB_CASSANDRA_LOCALDATACENTER",
"keyspace": "DB_CASSANDRA_KEYSPACE"
+ },
+ "postgres": {
+ "database": "DB_POSTGRES_DBNAME",
+ "host": "DB_POSTGRES_HOST",
+ "user": "DB_POSTGRES_USERNAME",
+ "password": "DB_POSTGRES_PASSWORD",
+ "port": "DB_POSTGRES_PORT",
+ "ssl": "DB_POSTGRES_USE_SSL"
}
},
"search": {
diff --git a/tdrive/backend/node/package-lock.json b/tdrive/backend/node/package-lock.json
index 902c5ca2..c4e96263 100644
--- a/tdrive/backend/node/package-lock.json
+++ b/tdrive/backend/node/package-lock.json
@@ -125,16 +125,6 @@
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
"dev": true
},
- "@babel/helper-function-name": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
- "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.22.5",
- "@babel/types": "^7.22.5"
- }
- },
"@babel/helper-hoist-variables": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
@@ -372,23 +362,112 @@
}
},
"@babel/traverse": {
- "version": "7.22.8",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz",
- "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==",
+ "version": "7.23.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz",
+ "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.22.5",
- "@babel/generator": "^7.22.7",
- "@babel/helper-environment-visitor": "^7.22.5",
- "@babel/helper-function-name": "^7.22.5",
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.22.7",
- "@babel/types": "^7.22.5",
- "debug": "^4.1.0",
+ "@babel/parser": "^7.23.6",
+ "@babel/types": "^7.23.6",
+ "debug": "^4.3.1",
"globals": "^11.1.0"
},
"dependencies": {
+ "@babel/code-frame": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
+ "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.23.4",
+ "chalk": "^2.4.2"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
+ "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.23.6",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
+ "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
+ "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
+ "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
+ }
+ },
+ "@babel/types": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
+ "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.23.4",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -425,6 +504,23 @@
"secure-json-parse": "^2.4.0"
}
},
+ "@emnapi/runtime": {
+ "version": "0.44.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.44.0.tgz",
+ "integrity": "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==",
+ "optional": true,
+ "requires": {
+ "tslib": "^2.4.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "optional": true
+ }
+ }
+ },
"@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -647,6 +743,147 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "@img/sharp-darwin-arm64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.1.tgz",
+ "integrity": "sha512-esr2BZ1x0bo+wl7Gx2hjssYhjrhUsD88VQulI0FrG8/otRQUOxLWHMBd1Y1qo2Gfg2KUvXNpT0ASnV9BzJCexw==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-darwin-arm64": "1.0.0"
+ }
+ },
+ "@img/sharp-darwin-x64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.1.tgz",
+ "integrity": "sha512-YrnuB3bXuWdG+hJlXtq7C73lF8ampkhU3tMxg5Hh+E7ikxbUVOU9nlNtVTloDXz6pRHt2y2oKJq7DY/yt+UXYw==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-darwin-x64": "1.0.0"
+ }
+ },
+ "@img/sharp-libvips-darwin-arm64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.0.tgz",
+ "integrity": "sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw==",
+ "optional": true
+ },
+ "@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.0.tgz",
+ "integrity": "sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linux-arm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.0.tgz",
+ "integrity": "sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.0.tgz",
+ "integrity": "sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.0.tgz",
+ "integrity": "sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linux-x64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.0.tgz",
+ "integrity": "sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.0.tgz",
+ "integrity": "sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ==",
+ "optional": true
+ },
+ "@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.0.tgz",
+ "integrity": "sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg==",
+ "optional": true
+ },
+ "@img/sharp-linux-arm": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.1.tgz",
+ "integrity": "sha512-Ii4X1vnzzI4j0+cucsrYA5ctrzU9ciXERfJR633S2r39CiD8npqH2GMj63uFZRCFt3E687IenAdbwIpQOJ5BNA==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linux-arm": "1.0.0"
+ }
+ },
+ "@img/sharp-linux-arm64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.1.tgz",
+ "integrity": "sha512-59B5GRO2d5N3tIfeGHAbJps7cLpuWEQv/8ySd9109ohQ3kzyCACENkFVAnGPX00HwPTQcaBNF7HQYEfZyZUFfw==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linux-arm64": "1.0.0"
+ }
+ },
+ "@img/sharp-linux-s390x": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.1.tgz",
+ "integrity": "sha512-tRGrb2pHnFUXpOAj84orYNxHADBDIr0J7rrjwQrTNMQMWA4zy3StKmMvwsI7u3dEZcgwuMMooIIGWEWOjnmG8A==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linux-s390x": "1.0.0"
+ }
+ },
+ "@img/sharp-linux-x64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.1.tgz",
+ "integrity": "sha512-4y8osC0cAc1TRpy02yn5omBeloZZwS62fPZ0WUAYQiLhSFSpWJfY/gMrzKzLcHB9ulUV6ExFiu2elMaixKDbeg==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linux-x64": "1.0.0"
+ }
+ },
+ "@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.1.tgz",
+ "integrity": "sha512-D3lV6clkqIKUizNS8K6pkuCKNGmWoKlBGh5p0sLO2jQERzbakhu4bVX1Gz+RS4vTZBprKlWaf+/Rdp3ni2jLfA==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.0"
+ }
+ },
+ "@img/sharp-linuxmusl-x64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.1.tgz",
+ "integrity": "sha512-LOGKNu5w8uu1evVqUAUKTix2sQu1XDRIYbsi5Q0c/SrXhvJ4QyOx+GaajxmOg5PZSsSnCYPSmhjHHsRBx06/wQ==",
+ "optional": true,
+ "requires": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.0"
+ }
+ },
+ "@img/sharp-wasm32": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.1.tgz",
+ "integrity": "sha512-vWI/sA+0p+92DLkpAMb5T6I8dg4z2vzCUnp8yvxHlwBpzN8CIcO3xlSXrLltSvK6iMsVMNswAv+ub77rsf25lA==",
+ "optional": true,
+ "requires": {
+ "@emnapi/runtime": "^0.44.0"
+ }
+ },
+ "@img/sharp-win32-ia32": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.1.tgz",
+ "integrity": "sha512-/xhYkylsKL05R+NXGJc9xr2Tuw6WIVl2lubFJaFYfW4/MQ4J+dgjIo/T4qjNRizrqs/szF/lC9a5+updmY9jaQ==",
+ "optional": true
+ },
+ "@img/sharp-win32-x64": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.1.tgz",
+ "integrity": "sha512-XaM69X0n6kTEsp9tVYYLhXdg7Qj32vYJlAKRutxUsm1UlgQNx6BOhHwZPwukCGXBU2+tH87ip2eV1I/E8MQnZg==",
+ "optional": true
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1323,6 +1560,15 @@
"make-plural": "^7.0.0"
}
},
+ "@mongodb-js/saslprep": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz",
+ "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==",
+ "optional": true,
+ "requires": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1879,6 +2125,70 @@
"resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.1.tgz",
"integrity": "sha512-lDBKAslCwvfK2uvS1Uk+UCpGvw+JRy5vnBFANPKFSY92n/iEnunXi0KVBjPJXhsM4jtdcPnS7tuZ0zjA9x6piQ=="
},
+ "@types/pg": {
+ "version": "8.10.9",
+ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
+ "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "pg-protocol": "*",
+ "pg-types": "^4.0.1"
+ },
+ "dependencies": {
+ "pg-types": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz",
+ "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==",
+ "dev": true,
+ "requires": {
+ "pg-int8": "1.0.1",
+ "pg-numeric": "1.0.2",
+ "postgres-array": "~3.0.1",
+ "postgres-bytea": "~3.0.0",
+ "postgres-date": "~2.0.1",
+ "postgres-interval": "^3.0.0",
+ "postgres-range": "^1.1.1"
+ }
+ },
+ "postgres-array": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
+ "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
+ "dev": true
+ },
+ "postgres-bytea": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
+ "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
+ "dev": true,
+ "requires": {
+ "obuf": "~1.1.2"
+ }
+ },
+ "postgres-date": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz",
+ "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==",
+ "dev": true
+ },
+ "postgres-interval": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
+ "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
+ "dev": true
+ }
+ }
+ },
+ "@types/pg-pool": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz",
+ "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==",
+ "dev": true,
+ "requires": {
+ "@types/pg": "*"
+ }
+ },
"@types/prettier": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
@@ -2007,9 +2317,9 @@
"dev": true
},
"@types/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
},
"@types/whatwg-url": {
"version": "8.2.2",
@@ -2143,6 +2453,12 @@
"eslint-visitor-keys": "^3.3.0"
}
},
+ "@zxing/text-encoding": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
+ "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
+ "optional": true
+ },
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -2469,6 +2785,11 @@
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
},
+ "available-typed-arrays": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
+ },
"avvio": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/avvio/-/avvio-7.2.5.tgz",
@@ -2743,6 +3064,11 @@
"fill-range": "^7.0.1"
}
},
+ "browser-or-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz",
+ "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg=="
+ },
"browserslist": {
"version": "4.21.9",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
@@ -2774,9 +3100,9 @@
}
},
"bson": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz",
- "integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA=="
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
+ "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g=="
},
"buffer": {
"version": "5.7.1",
@@ -2808,6 +3134,11 @@
"resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz",
"integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg=="
},
+ "buffer-writer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
+ },
"cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
@@ -2827,6 +3158,23 @@
"responselike": "^2.0.0"
}
},
+ "call-bind": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
+ "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
+ "requires": {
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.1",
+ "set-function-length": "^1.1.1"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ }
+ }
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2886,17 +3234,27 @@
}
},
"chai": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
- "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
+ "version": "4.3.10",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
+ "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==",
"requires": {
"assertion-error": "^1.1.0",
- "check-error": "^1.0.2",
- "deep-eql": "^4.1.2",
- "get-func-name": "^2.0.0",
- "loupe": "^2.3.1",
+ "check-error": "^1.0.3",
+ "deep-eql": "^4.1.3",
+ "get-func-name": "^2.0.2",
+ "loupe": "^2.3.6",
"pathval": "^1.1.1",
- "type-detect": "^4.0.5"
+ "type-detect": "^4.0.8"
+ },
+ "dependencies": {
+ "check-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+ "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+ "requires": {
+ "get-func-name": "^2.0.2"
+ }
+ }
}
},
"chalk": {
@@ -2920,11 +3278,6 @@
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
},
- "check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA=="
- },
"cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -3326,9 +3679,9 @@
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
},
"crypto-js": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
- "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"css-select": {
"version": "5.1.0",
@@ -3398,6 +3751,11 @@
}
}
},
+ "decode-uri-component": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
+ },
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -3427,11 +3785,6 @@
"type-detect": "^4.0.0"
}
},
- "deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
- },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3468,6 +3821,16 @@
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
},
+ "define-data-property": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
+ "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+ "requires": {
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ }
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3723,11 +4086,6 @@
"is-arrayish": "^0.2.1"
}
},
- "es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
- },
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
@@ -4100,11 +4458,6 @@
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
"dev": true
},
- "expand-template": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
- },
"expect": {
"version": "29.6.1",
"resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz",
@@ -4200,11 +4553,11 @@
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
},
"fast-xml-parser": {
- "version": "3.21.1",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz",
- "integrity": "sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
+ "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
"requires": {
- "strnum": "^1.0.4"
+ "strnum": "^1.0.5"
}
},
"fastfall": {
@@ -4558,6 +4911,11 @@
"to-regex-range": "^5.0.1"
}
},
+ "filter-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+ "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
+ },
"find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -4608,6 +4966,14 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
+ "for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "requires": {
+ "is-callable": "^1.1.3"
+ }
+ },
"form-auto-content": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/form-auto-content/-/form-auto-content-2.2.1.tgz",
@@ -4737,9 +5103,27 @@
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="
+ },
+ "get-intrinsic": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
+ "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
+ "requires": {
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ }
+ }
},
"get-package-type": {
"version": "0.1.0",
@@ -4802,11 +5186,6 @@
}
}
},
- "github-from-package": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
- },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -4881,6 +5260,14 @@
}
}
},
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
"got": {
"version": "11.8.6",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
@@ -4941,11 +5328,52 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
+ "has-property-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
+ "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+ "requires": {
+ "get-intrinsic": "^1.2.2"
+ }
+ },
+ "has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+ },
+ "has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
+ "hasown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
+ "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ }
+ }
+ },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -5245,11 +5673,6 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
- "ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
- },
"ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
@@ -5260,6 +5683,15 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
+ "is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -5279,6 +5711,11 @@
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
+ "is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
+ },
"is-core-module": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
@@ -5305,6 +5742,14 @@
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
"dev": true
},
+ "is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -5352,6 +5797,14 @@
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true
},
+ "is-typed-array": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+ "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+ "requires": {
+ "which-typed-array": "^1.1.11"
+ }
+ },
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -7352,31 +7805,30 @@
}
},
"minio": {
- "version": "7.0.18",
- "resolved": "https://registry.npmjs.org/minio/-/minio-7.0.18.tgz",
- "integrity": "sha512-jVRjkw8A5Spf+ETY5OXQUcQckHriuUA3u2+MAcX36btLT8EytlOVivxIseXvyFf9cNn3dy5w1F1UyjMvHU+nqg==",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minio/-/minio-7.1.3.tgz",
+ "integrity": "sha512-xPrLjWkTT5E7H7VnzOjF//xBp9I40jYB4aWhb2xTFopXXfw+Wo82DDWngdUju7Doy3Wk7R8C4LAgwhLHHnf0wA==",
"requires": {
- "async": "^3.1.0",
- "block-stream2": "^2.0.0",
- "es6-error": "^4.1.1",
- "fast-xml-parser": "^3.17.5",
+ "async": "^3.2.4",
+ "block-stream2": "^2.1.0",
+ "browser-or-node": "^2.1.1",
+ "buffer-crc32": "^0.2.13",
+ "fast-xml-parser": "^4.2.2",
+ "ipaddr.js": "^2.0.1",
"json-stream": "^1.0.0",
- "lodash": "^4.17.20",
- "mime-types": "^2.1.14",
- "mkdirp": "^0.5.1",
- "querystring": "0.2.0",
- "through2": "^3.0.1",
- "xml": "^1.0.0",
- "xml2js": "^0.4.15"
+ "lodash": "^4.17.21",
+ "mime-types": "^2.1.35",
+ "query-string": "^7.1.3",
+ "through2": "^4.0.2",
+ "web-encoding": "^1.1.5",
+ "xml": "^1.0.1",
+ "xml2js": "^0.5.0"
},
"dependencies": {
- "mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "requires": {
- "minimist": "^1.2.6"
- }
+ "ipaddr.js": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz",
+ "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ=="
}
}
},
@@ -7409,24 +7861,19 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
- "mkdirp-classic": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
- },
"moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"mongodb": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.6.0.tgz",
- "integrity": "sha512-z8qVs9NfobHJm6uzK56XBZF8XwM9H294iRnB7wNjF0SnY93si5HPziIJn+qqvUR5QOff/4L0gCD6SShdR/GtVQ==",
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
+ "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
"requires": {
- "bson": "^5.3.0",
+ "@mongodb-js/saslprep": "^1.1.0",
+ "bson": "^5.5.0",
"mongodb-connection-string-url": "^2.6.0",
- "saslprep": "^1.0.3",
"socks": "^2.7.1"
}
},
@@ -7487,11 +7934,6 @@
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="
},
- "napi-build-utils": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
- "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
- },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -7563,24 +8005,6 @@
}
}
},
- "node-abi": {
- "version": "3.45.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.45.0.tgz",
- "integrity": "sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ==",
- "requires": {
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
"node-abort-controller": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
@@ -7816,6 +8240,12 @@
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
},
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
"oidc-token-hash": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
@@ -8019,6 +8449,11 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
+ "packet-reader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
+ },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -8146,6 +8581,73 @@
"gm": "^1.23.1"
}
},
+ "pg": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
+ "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
+ "requires": {
+ "buffer-writer": "2.0.0",
+ "packet-reader": "1.0.0",
+ "pg-cloudflare": "^1.1.1",
+ "pg-connection-string": "^2.6.2",
+ "pg-pool": "^3.6.1",
+ "pg-protocol": "^1.6.0",
+ "pg-types": "^2.1.0",
+ "pgpass": "1.x"
+ }
+ },
+ "pg-cloudflare": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
+ "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
+ "optional": true
+ },
+ "pg-connection-string": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
+ "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
+ },
+ "pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
+ },
+ "pg-numeric": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
+ "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
+ "dev": true
+ },
+ "pg-pool": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
+ "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og=="
+ },
+ "pg-protocol": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
+ "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
+ },
+ "pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "requires": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ }
+ },
+ "pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "requires": {
+ "split2": "^4.1.0"
+ }
+ },
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -8335,25 +8837,35 @@
}
}
},
- "prebuild-install": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
- "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+ "postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
+ },
+ "postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
+ },
+ "postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
+ },
+ "postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"requires": {
- "detect-libc": "^2.0.0",
- "expand-template": "^2.0.3",
- "github-from-package": "0.0.0",
- "minimist": "^1.2.3",
- "mkdirp-classic": "^0.5.3",
- "napi-build-utils": "^1.0.1",
- "node-abi": "^3.3.0",
- "pump": "^3.0.0",
- "rc": "^1.2.7",
- "simple-get": "^4.0.0",
- "tar-fs": "^2.0.0",
- "tunnel-agent": "^0.6.0"
+ "xtend": "^4.0.0"
}
},
+ "postgres-range": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz",
+ "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==",
+ "dev": true
+ },
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -8492,10 +9004,16 @@
"integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==",
"dev": true
},
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="
+ "query-string": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
+ "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
+ "requires": {
+ "decode-uri-component": "^0.2.2",
+ "filter-obj": "^1.1.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
+ }
},
"querystringify": {
"version": "2.2.0",
@@ -8564,17 +9082,6 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- }
- },
"react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -8854,15 +9361,6 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
- "saslprep": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
- "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
- "optional": true,
- "requires": {
- "sparse-bitfield": "^3.0.3"
- }
- },
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -8885,7 +9383,6 @@
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
@@ -8942,33 +9439,55 @@
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
},
+ "set-function-length": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
+ "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+ "requires": {
+ "define-data-property": "^1.1.1",
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ }
+ },
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"sharp": {
- "version": "0.30.7",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz",
- "integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==",
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.1.tgz",
+ "integrity": "sha512-iAYUnOdTqqZDb3QjMneBKINTllCJDZ3em6WaWy7NPECM4aHncvqHRm0v0bN9nqJxMiwamv5KIdauJ6lUzKDpTQ==",
"requires": {
+ "@img/sharp-darwin-arm64": "0.33.1",
+ "@img/sharp-darwin-x64": "0.33.1",
+ "@img/sharp-libvips-darwin-arm64": "1.0.0",
+ "@img/sharp-libvips-darwin-x64": "1.0.0",
+ "@img/sharp-libvips-linux-arm": "1.0.0",
+ "@img/sharp-libvips-linux-arm64": "1.0.0",
+ "@img/sharp-libvips-linux-s390x": "1.0.0",
+ "@img/sharp-libvips-linux-x64": "1.0.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.0",
+ "@img/sharp-linux-arm": "0.33.1",
+ "@img/sharp-linux-arm64": "0.33.1",
+ "@img/sharp-linux-s390x": "0.33.1",
+ "@img/sharp-linux-x64": "0.33.1",
+ "@img/sharp-linuxmusl-arm64": "0.33.1",
+ "@img/sharp-linuxmusl-x64": "0.33.1",
+ "@img/sharp-wasm32": "0.33.1",
+ "@img/sharp-win32-ia32": "0.33.1",
+ "@img/sharp-win32-x64": "0.33.1",
"color": "^4.2.3",
- "detect-libc": "^2.0.1",
- "node-addon-api": "^5.0.0",
- "prebuild-install": "^7.1.1",
- "semver": "^7.3.7",
- "simple-get": "^4.0.1",
- "tar-fs": "^2.1.1",
- "tunnel-agent": "^0.6.0"
+ "detect-libc": "^2.0.2",
+ "semver": "^7.5.4"
},
"dependencies": {
- "semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
+ "detect-libc": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
+ "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="
}
}
},
@@ -9001,21 +9520,6 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
- "simple-concat": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
- },
- "simple-get": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
- "requires": {
- "decompress-response": "^6.0.0",
- "once": "^1.3.1",
- "simple-concat": "^1.0.0"
- }
- },
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -9225,11 +9729,15 @@
"through": "2"
}
},
+ "split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+ },
"split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
- "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
- "dev": true
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
},
"sprintf-js": {
"version": "1.0.3",
@@ -9308,6 +9816,11 @@
"resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz",
"integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew=="
},
+ "strict-uri-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
+ },
"string-argv": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz",
@@ -9378,7 +9891,8 @@
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true
},
"strnum": {
"version": "1.0.5",
@@ -9412,24 +9926,6 @@
"yallist": "^4.0.0"
}
},
- "tar-fs": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
- "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
- "requires": {
- "chownr": "^1.1.1",
- "mkdirp-classic": "^0.5.2",
- "pump": "^3.0.0",
- "tar-stream": "^2.1.4"
- },
- "dependencies": {
- "chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
- }
- }
- },
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
@@ -9471,12 +9967,11 @@
"dev": true
},
"through2": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
- "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+ "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
"requires": {
- "inherits": "^2.0.4",
- "readable-stream": "2 || 3"
+ "readable-stream": "3"
}
},
"tiny-lru": {
@@ -9730,14 +10225,6 @@
"tslib": "^1.8.1"
}
},
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
- "requires": {
- "safe-buffer": "^5.0.1"
- }
- },
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -9858,6 +10345,18 @@
"requires-port": "^1.0.0"
}
},
+ "util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -9924,6 +10423,15 @@
"defaults": "^1.0.3"
}
},
+ "web-encoding": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz",
+ "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==",
+ "requires": {
+ "@zxing/text-encoding": "0.9.0",
+ "util": "^0.12.3"
+ }
+ },
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -9946,6 +10454,18 @@
"isexe": "^2.0.0"
}
},
+ "which-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
+ "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
+ "requires": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.4",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
"wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -10044,9 +10564,9 @@
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
},
"xml2js": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
- "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+ "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
diff --git a/tdrive/backend/node/package.json b/tdrive/backend/node/package.json
index 95b47665..0ea10ac2 100644
--- a/tdrive/backend/node/package.json
+++ b/tdrive/backend/node/package.json
@@ -74,6 +74,7 @@
"@types/node-fetch": "^2.5.12",
"@types/node-uuid": "^0.0.28",
"@types/pdf-image": "^2.0.1",
+ "@types/pg-pool": "^2.0.6",
"@types/pump": "^1.1.1",
"@types/random-useragent": "^0.3.1",
"@types/socket.io-client": "^1.4.36",
@@ -153,9 +154,9 @@
"jsonwebtoken": "^9.0.0",
"jwks-rsa": "^3.0.1",
"lodash": "^4.17.21",
- "minio": "7.0.18",
+ "minio": "^7.1.3",
"moment": "^2.29.4",
- "mongodb": "^5.6.0",
+ "mongodb": "^5.9.2",
"multistream": "^4.1.0",
"njwt": "^2.0.0",
"node-abort-controller": "^3.1.1",
@@ -167,6 +168,8 @@
"ora": "^5.4.0",
"pdf-parse": "^1.1.1",
"pdf2pic": "^2.1.4",
+ "pg": "^8.11.3",
+ "pg-pool": "^3.6.1",
"pino": "^6.8.0",
"pino-std-serializers": "^6.1.0",
"probe-image-size": "^7.2.3",
@@ -175,7 +178,7 @@
"redis": "3",
"reflect-metadata": "^0.1.13",
"rxjs": "^6.6.3",
- "sharp": "^0.30.5",
+ "sharp": "^0.33.1",
"short-uuid": "^4.2.2",
"socket.io": "^4.6.2",
"socket.io-client": "^3.0.0",
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/connector-factory.ts b/tdrive/backend/node/src/core/platform/services/database/services/connector-factory.ts
index 9c12f1b9..933f0e12 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/connector-factory.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/connector-factory.ts
@@ -5,6 +5,7 @@ import {
CassandraConnector,
} from "./orm/connectors/cassandra/cassandra";
import { MongoConnectionOptions, MongoConnector } from "./orm/connectors/mongodb/mongodb";
+import { PostgresConnectionOptions, PostgresConnector } from "./orm/connectors/postgres/postgres";
export class ConnectorFactory {
public create(type: DatabaseType, options: ConnectionOptions, secret: string): Connector {
@@ -13,6 +14,8 @@ export class ConnectorFactory {
return new CassandraConnector(type, options as CassandraConnectionOptions, secret);
case "mongodb":
return new MongoConnector(type, options as MongoConnectionOptions, secret);
+ case "postgres":
+ return new PostgresConnector(type, options as PostgresConnectionOptions, secret);
default:
throw new Error(`${type} is not supported`);
}
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/index.ts b/tdrive/backend/node/src/core/platform/services/database/services/index.ts
index fe3a0914..4b6066be 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/index.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/index.ts
@@ -7,6 +7,7 @@ import { CassandraConnectionOptions } from "./orm/connectors/cassandra/cassandra
import { MongoConnectionOptions } from "./orm/connectors/mongodb/mongodb";
import { EntityTarget } from "./orm/types";
import { RepositoryManager } from "./orm/repository/manager";
+import { PostgresConnectionOptions } from "./orm/connectors/postgres/postgres";
export default class DatabaseService implements DatabaseServiceAPI {
version = "1";
@@ -40,6 +41,9 @@ export default class DatabaseService implements DatabaseServiceAPI {
}
}
-export declare type ConnectionOptions = MongoConnectionOptions | CassandraConnectionOptions;
+export declare type ConnectionOptions =
+ | MongoConnectionOptions
+ | CassandraConnectionOptions
+ | PostgresConnectionOptions;
-export declare type DatabaseType = "mongodb" | "cassandra";
+export declare type DatabaseType = "mongodb" | "cassandra" | "postgres";
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/abstract-connector.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/abstract-connector.ts
index 92b41c27..bcbdd4d6 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/abstract-connector.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/abstract-connector.ts
@@ -1,27 +1,23 @@
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */
-import { Connector } from ".";
+import { Connector, UpsertOptions } from ".";
import { ConnectionOptions, DatabaseType } from "../..";
import { FindOptions } from "../repository/repository";
import { ColumnDefinition, EntityDefinition } from "../types";
import { ListResult } from "../../../../../framework/api/crud-service";
-export abstract class AbstractConnector
- implements Connector
-{
+export abstract class AbstractConnector implements Connector {
constructor(protected type: DatabaseType, protected options: T, protected secret: string) {}
abstract connect(): Promise;
abstract drop(): Promise;
- abstract getClient(): DatabaseClient;
-
abstract createTable(
entity: EntityDefinition,
columns: { [name: string]: ColumnDefinition },
): Promise;
- abstract upsert(entities: any[]): Promise;
+ abstract upsert(entities: any[], _options: UpsertOptions): Promise;
abstract remove(entities: any[]): Promise;
@@ -38,8 +34,4 @@ export abstract class AbstractConnector {
+export class CassandraConnector extends AbstractConnector {
private client: cassandra.Client;
private keyspaceExists = false;
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/index.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/index.ts
index a0369ed0..175b944e 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/index.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/index.ts
@@ -5,11 +5,14 @@ import { MongoConnectionOptions } from "./mongodb/mongodb";
import { ColumnDefinition, EntityDefinition } from "../types";
import { FindOptions } from "../repository/repository";
import { ListResult } from "../../../../../framework/api/crud-service";
+import { PostgresConnectionOptions } from "./postgres/postgres";
export * from "./mongodb/mongodb";
export * from "./cassandra/cassandra";
-export type UpsertOptions = any;
+export type UpsertOptions = {
+ action?: "INSERT" | "UPDATE";
+};
export type RemoveOptions = any;
@@ -41,7 +44,7 @@ export interface Connector extends Initializable {
* Upsert
* returns true if the object was created/updated, false otherwise
*/
- upsert(entities: any[]): Promise;
+ upsert(entities: any[], _options: UpsertOptions): Promise;
/**
* Remove
@@ -60,4 +63,7 @@ export interface Connector extends Initializable {
): Promise>;
}
-export declare type ConnectionOptions = MongoConnectionOptions | CassandraConnectionOptions;
+export declare type ConnectionOptions =
+ | MongoConnectionOptions
+ | CassandraConnectionOptions
+ | PostgresConnectionOptions;
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/mongodb.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/mongodb.ts
index c8ffe2c4..4a627014 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/mongodb.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/mongodb/mongodb.ts
@@ -9,15 +9,13 @@ import { buildSelectQuery } from "./query-builder";
import { transformValueFromDbString, transformValueToDbString } from "./typeTransforms";
import { logger } from "../../../../../../framework";
-export { MongoPagination } from "./pagination";
-
export interface MongoConnectionOptions {
// TODO: More options
uri: string;
database: string;
}
-export class MongoConnector extends AbstractConnector {
+export class MongoConnector extends AbstractConnector {
private client: mongo.MongoClient;
async init(): Promise {
@@ -75,6 +73,7 @@ export class MongoConnector extends AbstractConnector {
+ logger.trace(`services.database.orm.mongodb.upsert - entities count: ${entities.length}`);
return new Promise(async resolve => {
const promises: Promise[] = [];
@@ -83,6 +82,11 @@ export class MongoConnector extends AbstractConnector {
const { columnsDefinition, entityDefinition } = getEntityDefinition(entity);
const primaryKey = unwrapPrimarykey(entityDefinition);
+ logger.trace(
+ `services.database.orm.mongodb.upsert[${_options.action}] - Entity{${
+ entityDefinition.name
+ }: ${JSON.stringify(entity)}`,
+ );
//Set updated content
const set: any = {};
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-data-transform.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-data-transform.ts
new file mode 100644
index 00000000..0e0d4d6e
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-data-transform.ts
@@ -0,0 +1,156 @@
+import { ColumnType } from "../../../orm/types";
+import { isBoolean, isInteger, isNull, isString, isUndefined } from "lodash";
+import { decrypt, encrypt } from "../../../../../../../crypto";
+import { logger } from "../../../../../../framework";
+
+export type TransformOptions = {
+ secret?: string;
+};
+
+export const TypeMappings = {
+ encoded_string: "TEXT",
+ encoded_json: "TEXT",
+ json: "TEXT",
+ string: "TEXT",
+ number: "BIGINT",
+ timeuuid: "UUID",
+ uuid: "UUID",
+ boolean: "BOOLEAN",
+ counter: "BIGINT",
+
+ // backward compatibility
+ tdrive_boolean: "BOOLEAN",
+ tdrive_datetime: "BIGINT", //Deprecated
+};
+
+/**
+ * Possible data transformations:
+ * string --> everything
+ * encoded_string --> everything
+ * json --> everything
+ * encoded_json --> everything
+ *
+ * number --> number, string
+ * tdrive_int --> number, string
+ * tdrive_datetime --> number. string
+ *
+ * timeuuid --> string
+ * uuid --> string
+ *
+ * boolean --> boolean, number
+ * tdrive_boolean --> boolean, number
+ *
+ * tdrive_datetime --> number
+ *
+ * null and undefined --> null
+ */
+export class PostgresDataTransformer {
+ constructor(readonly options: TransformOptions) {}
+
+ fromDbString(v: any, type: ColumnType): any {
+ if (isNull(v) || isUndefined(v)) {
+ return null;
+ }
+
+ this.checkType(type);
+
+ if (v !== null && (type === "encoded_string" || type === "encoded_json")) {
+ let decryptedValue: string;
+
+ if (typeof v === "string" && v.trim() === "") {
+ return v;
+ }
+
+ try {
+ decryptedValue = decrypt(v, this.options.secret).data;
+ } catch (err) {
+ logger.debug(`Can not decrypt data (${err.message}) %o of type ${type}`, v);
+
+ decryptedValue = v;
+ }
+
+ if (type === "encoded_json") {
+ try {
+ decryptedValue = JSON.parse(decryptedValue);
+ } catch (err) {
+ logger.debug(
+ { err },
+ `Can not parse JSON from decrypted data %o of type ${type}`,
+ decryptedValue,
+ );
+ decryptedValue = null;
+ }
+ }
+
+ return decryptedValue;
+ }
+
+ if (type === "number" || type === "tdrive_datetime" || type === "counter") {
+ const number = parseInt(v, 10);
+ if (isNaN(number)) throw new Error(`Can't parse ${v} to int`);
+ return number;
+ }
+
+ if (type === "json") {
+ return JSON.parse(v);
+ }
+
+ return v;
+ }
+
+ toDbString(v: any, type: ColumnType): any {
+ this.checkType(type);
+
+ if (isNull(v) || isUndefined(v)) {
+ return null;
+ }
+
+ if (type === "number" || type === "tdrive_datetime" || type === "counter") {
+ const number = parseInt(v, 10);
+ if (isNaN(number)) throw new Error(`Can't parse ${v} to int`);
+ return number;
+ }
+
+ if (type === "uuid" || type === "timeuuid") {
+ if (!isString(v))
+ throw new Error(`uuid or timeuuid could be only strings, and ${v} is not string`);
+ return v;
+ }
+
+ if (type === "boolean" || type === "tdrive_boolean") {
+ //Security to avoid string with "false" in it
+ if (!isInteger(v) && !isBoolean(v)) {
+ throw new Error(`'${v}' is not a ${type}`);
+ }
+ return !!v;
+ }
+
+ if (type === "string") {
+ if (isString(v)) return v;
+ else throw "We can't store in string fields only strings";
+ }
+
+ if (type === "json") {
+ return JSON.stringify(v);
+ }
+
+ if (type === "encoded_string" || type === "encoded_json") {
+ if (type === "encoded_string" && !isString(v))
+ throw "We can't store in string fields only strings";
+
+ if (type === "encoded_json") {
+ v = JSON.stringify(v);
+ }
+ const encrypted = encrypt(v, this.options.secret);
+ return `${(encrypted.data || "").toString().replace(/'/gm, "''")}`;
+ }
+
+ return v.toString();
+ }
+
+ private checkType(type: ColumnType) {
+ if (!Object.keys(TypeMappings).includes(type)) {
+ throw new Error(`Support of ${type} not implemented yet for PostgreSQL`);
+ }
+ }
+}
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-query-builder.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-query-builder.ts
new file mode 100644
index 00000000..cde84651
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres-query-builder.ts
@@ -0,0 +1,185 @@
+import { comparisonType, FindOptions } from "../../repository/repository";
+import { ObjectType } from "../../types";
+import { getEntityDefinition, unwrapPrimarykey } from "../../utils";
+import { PostgresDataTransformer } from "./postgres-data-transform";
+import _ from "lodash";
+
+export type Query = [string, any[]];
+
+export class PostgresQueryBuilder {
+ private dataTransformer: PostgresDataTransformer;
+
+ constructor(private secret: string) {
+ this.dataTransformer = new PostgresDataTransformer({ secret: this.secret });
+ }
+
+ buildSelect(
+ entityType: ObjectType,
+ filters: Record,
+ options: FindOptions,
+ ): Query {
+ const instance = new (entityType as any)();
+ const { columnsDefinition, entityDefinition } = getEntityDefinition(instance);
+
+ const query = (whereClause: string, orderByClause: string, limit: number, offset: number) => {
+ return `SELECT * FROM "${entityDefinition.name}"
+ ${whereClause?.length ? "WHERE " + whereClause : ""}
+ ${orderByClause?.length ? "ORDER BY " + orderByClause : ""}
+ LIMIT ${limit} OFFSET ${offset}`;
+ };
+
+ // === EQUAL or IN operator ===
+ let idx = 1;
+ const values = [];
+ let whereClause = "";
+ if (filters) {
+ Object.keys(filters)
+ .filter(key => !_.isUndefined(filters[key]))
+ .forEach(key => {
+ const filter = filters[key];
+ if (Array.isArray(filter)) {
+ if (filter.length) {
+ const inClause: string[] = filter.map(
+ value => `${this.dataTransformer.toDbString(value, columnsDefinition[key].type)}`,
+ );
+
+ whereClause += `${key} IN ($${inClause.map(() => idx++).join(",$")}) AND `;
+ values.push(...inClause);
+ }
+ } else {
+ const value = `${this.dataTransformer.toDbString(filter, columnsDefinition[key].type)}`;
+ whereClause += `${key} = $${idx++} AND `;
+ values.push(value);
+ }
+ });
+ }
+
+ if (options) {
+ // ==== Comparison operators ===
+ const appendComparison = (predicates: comparisonType[], operator: string) => {
+ if (predicates) {
+ predicates.forEach(element => {
+ whereClause += `${element[0]} ${operator} $${idx++} AND `;
+ values.push(
+ this.dataTransformer.toDbString(element[1], columnsDefinition[element[0]].type),
+ );
+ });
+ }
+ };
+
+ appendComparison(options.$lt, "<");
+ appendComparison(options.$lte, "<=");
+ appendComparison(options.$gt, ">");
+ appendComparison(options.$gte, ">=");
+
+ // === IN ===
+ options.$in?.forEach(e => {
+ whereClause += `${e[0]} IN ($${e[1].map(() => idx++).join(",$")}) AND `;
+ values.push(...e[1]);
+ });
+
+ // === LIKE ====
+ options.$like?.forEach(e => {
+ whereClause += `${e[0]} LIKE $${idx++} AND `;
+ values.push(`%${e[1]}%`);
+ });
+ }
+
+ if (whereClause && whereClause.endsWith("AND ")) whereClause = whereClause.slice(0, -4);
+
+ // ==== ORDER BY =====
+ const orderByClause = `${entityDefinition.options.primaryKey
+ .slice(1)
+ .map(
+ (key: string) =>
+ `${key} ${(columnsDefinition[key].options.order || "ASC") === "ASC" ? "DESC" : "ASC"}`,
+ )}`;
+
+ // ==== PAGING =====
+ let limit = 100;
+ let offset = 0;
+ if (options?.pagination) {
+ if (options.pagination.limitStr) {
+ limit = Number.parseInt(options.pagination.limitStr);
+ }
+ if (options.pagination.page_token) {
+ offset = (Number.parseInt(options.pagination.page_token) - 1) * limit;
+ }
+ }
+ return [query(whereClause, orderByClause, limit, offset), values];
+ }
+
+ buildDelete(entity: Entity): Query {
+ const { columnsDefinition, entityDefinition } = getEntityDefinition(entity);
+ const primaryKey = unwrapPrimarykey(entityDefinition);
+
+ const toValueKeyDBStringPair = (key: string) => {
+ return [
+ key,
+ this.dataTransformer.toDbString(
+ entity[columnsDefinition[key].nodename],
+ columnsDefinition[key].type,
+ ),
+ ];
+ };
+
+ const where = primaryKey.map(key => toValueKeyDBStringPair(key));
+ const query = `DELETE FROM "${entityDefinition.name}"
+ WHERE ${where.map((e, idx) => `${e[0]} = $${idx + 1}`).join(" AND ")}`;
+
+ return [query, where.map(f => f[1])];
+ }
+
+ buildInsert(entity: Entity): Query {
+ const { columnsDefinition, entityDefinition } = getEntityDefinition(entity);
+ const toValueKeyDBStringPair = (key: string) => {
+ return [
+ key,
+ this.dataTransformer.toDbString(
+ entity[columnsDefinition[key].nodename],
+ columnsDefinition[key].type,
+ ),
+ ];
+ };
+
+ const fields = Object.keys(columnsDefinition)
+ .filter(key => entity[columnsDefinition[key].nodename] !== undefined)
+ .map(key => toValueKeyDBStringPair(key));
+ const query = `INSERT INTO "${entityDefinition.name}" (${fields.map(e => e[0]).join(", ")})
+ VALUES (${fields.map((e, idx) => `$${idx + 1}`).join(", ")})`;
+ return [query, fields.map(f => f[1])];
+ }
+
+ buildUpdate(entity: Entity): Query {
+ const { columnsDefinition, entityDefinition } = getEntityDefinition(entity);
+ const primaryKey = unwrapPrimarykey(entityDefinition);
+
+ const toValueKeyDBStringPair = (key: string) => {
+ return [
+ key,
+ this.dataTransformer.toDbString(
+ entity[columnsDefinition[key].nodename],
+ columnsDefinition[key].type,
+ ),
+ ];
+ };
+
+ // Set updated content
+ const set = Object.keys(columnsDefinition)
+ .filter(key => primaryKey.indexOf(key) === -1)
+ .filter(key => entity[columnsDefinition[key].nodename] !== undefined)
+ .map(key => toValueKeyDBStringPair(key));
+ //Set primary key
+ const where = primaryKey.map(key => toValueKeyDBStringPair(key));
+ //Start index for where clause params
+ const whereIdx = set.length + 1;
+ const query = `UPDATE "${entityDefinition.name}"
+ SET ${set.map((e, idx) => `${e[0]} = $${idx + 1}`).join(", ")}
+ WHERE ${where.map((e, idx) => `${e[0]} = $${whereIdx + idx}`).join(" AND ")}`;
+ const values = [];
+ values.push(...set.map(f => f[1]));
+ values.push(...where.map(f => f[1]));
+
+ return [query, values];
+ }
+}
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres.ts
new file mode 100644
index 00000000..06f82ce0
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/connectors/postgres/postgres.ts
@@ -0,0 +1,283 @@
+import { AbstractConnector } from "../abstract-connector";
+import { ColumnDefinition, EntityDefinition, ObjectType } from "../../types";
+import { ListResult, Paginable, Pagination } from "../../../../../../framework/api/crud-service";
+import { FindOptions } from "../../repository/repository";
+import { Client, QueryResult } from "pg";
+import { getLogger, logger } from "../../../../../../framework";
+import { getEntityDefinition } from "../../../orm/utils";
+import { UpsertOptions } from "src/core/platform/services/database/services/orm/connectors";
+import { PostgresDataTransformer, TypeMappings } from "./postgres-data-transform";
+import { PostgresQueryBuilder, Query } from "./postgres-query-builder";
+
+export interface PostgresConnectionOptions {
+ database: string;
+ user: string;
+ password: string;
+ port: number;
+ host: string;
+ ssl: false;
+ idleTimeoutMillis: 1000; // close idle clients after 1 second
+ connectionTimeoutMillis: 1000; // return an error after 1 second if connection could not be established
+ statement_timeout: number; // number of milliseconds before a statement in query will time out, default is no timeout
+ query_timeout: number; // number of milliseconds before a query call will time out, default is no timeout
+}
+
+export class PostgresConnector extends AbstractConnector {
+ private logger = getLogger("PostgresConnector");
+ private client: Client = new Client(this.options);
+ private dataTransformer = new PostgresDataTransformer({ secret: this.secret });
+ private queryBuilder = new PostgresQueryBuilder(this.secret);
+
+ private async healthcheck(): Promise {
+ const result: QueryResult = await this.client.query("SELECT NOW()");
+ if (!result || result.rowCount != 1) {
+ throw new Error("Connection Error");
+ }
+ this.logger.info(`DB connection is fine, current time is ${result.rows[0].now}`);
+ }
+
+ async connect(): Promise {
+ if (this.client) {
+ this.logger.info("Connecting to DB");
+ this.client.on("error", err => {
+ this.logger.warn(err, "PostgreSQL connection error");
+ });
+
+ await this.client.connect();
+ this.logger.info("Connection pool created");
+ await this.healthcheck();
+ }
+ return this;
+ }
+
+ async init(): Promise {
+ if (!this.client) {
+ await this.connect();
+ }
+ return this;
+ }
+
+ async createTable(
+ entity: EntityDefinition,
+ columns: { [p: string]: ColumnDefinition },
+ ): Promise {
+ const columnsString = Object.keys(columns)
+ .map(colName => {
+ const definition = columns[colName];
+ return `${colName} ${TypeMappings[definition.type]}`;
+ })
+ .join(",\n");
+
+ // --- Generate final create table query --- //
+ const query = `
+ CREATE TABLE IF NOT EXISTS "${entity.name}"
+ (
+ ${columnsString}
+ );`;
+
+ try {
+ this.logger.debug(
+ `service.database.orm.createTable - Creating table ${entity.name} : ${query}`,
+ );
+ const result: QueryResult = await this.client.query(query);
+ this.logger.info(`Table is created with the result ${JSON.stringify(result)}`);
+ } catch (err) {
+ this.logger.warn(
+ { err },
+ `service.database.orm.createTable - creation error for table ${entity.name} : ${err.message}`,
+ );
+ return false;
+ }
+
+ //--- Alter table if not up to date --- //
+ await this.alterTable(entity, columns);
+
+ // --- Create primary key --- //
+ await this.alterTablePrimaryKey(entity);
+
+ // --- Create indexes --- //
+ return await this.alterTableIndexes(entity);
+ }
+
+ private async alterTableIndexes(entity: EntityDefinition) {
+ if (entity.options.globalIndexes) {
+ for (const globalIndex of entity.options.globalIndexes) {
+ const indexName = globalIndex.join("_");
+ const indexDbName = `index_${entity.name}_${indexName}`;
+
+ const query = `CREATE INDEX IF NOT EXISTS ${indexDbName} ON "${entity.name}"
+ (${globalIndex.length === 1 ? globalIndex[0] : `(${globalIndex[0]}), ${globalIndex[1]}`})`;
+
+ try {
+ this.logger.debug(`Creating index ${indexName} (${indexDbName}) : ${query}`);
+ await this.client.query(query);
+ } catch (err) {
+ this.logger.warn(
+ err,
+ `Creation error for index ${indexName} (${indexDbName}) : ${err.message}`,
+ );
+ return false;
+ }
+ }
+ }
+ }
+
+ private async alterTablePrimaryKey(entity: EntityDefinition) {
+ if (entity.options.primaryKey) {
+ const query = `ALTER TABLE "${entity.name}" ADD PRIMARY KEY (
+ ${entity.options.primaryKey.join(", ")});`;
+ try {
+ await this.client.query(query);
+ } catch (err) {
+ this.logger.warn(err, `Error creating primary key for "${entity.name}"`);
+ }
+ }
+ }
+
+ private async alterTable(entity: EntityDefinition, columns: { [p: string]: ColumnDefinition }) {
+ const existingColumns = await this.getTableDefinition(entity.name);
+ if (existingColumns.length > 0) {
+ this.logger.debug(
+ `Existing columns for table "${entity.name}", generating alter table queries ...`,
+ );
+ const alterQueryColumns = Object.keys(columns)
+ .filter(colName => existingColumns.indexOf(colName) < 0)
+ .map(colName => `ADD COLUMN ${colName} ${TypeMappings[columns[colName].type]}`)
+ .join(", ");
+
+ if (alterQueryColumns.length > 0) {
+ const alterQuery = `ALTER TABLE "${entity.name}" ${alterQueryColumns}`;
+ const queryResult: QueryResult = await this.client.query(alterQuery);
+ this.logger.info(`Table is altered with the result ${queryResult}`);
+ }
+ }
+ }
+
+ async drop(): Promise {
+ logger.info("Drop database is not implemented for security reasons.");
+ return this;
+ }
+
+ async dropTables(): Promise {
+ const query = `
+ DO $$
+ DECLARE
+ tablename text;
+ BEGIN
+ FOR tablename IN (SELECT table_name FROM information_schema.tables WHERE table_schema = 'public')
+ LOOP
+ EXECUTE 'DROP TABLE IF EXISTS "' || tablename || '" CASCADE';
+ END LOOP;
+ END $$;`;
+ logger.debug(`service.database.orm.postgres.dropTables - Query: "${query}"`);
+ await this.client.query(query);
+ return this;
+ }
+
+ async find(
+ entityType: ObjectType,
+ filters: Record,
+ options: FindOptions,
+ ): Promise> {
+ const query = this.queryBuilder.buildSelect(entityType, filters, options);
+
+ logger.debug(`services.database.orm.postgres.find - Query: ${query}`);
+
+ const results = await this.client.query(query[0] as string, query[1] as never[]);
+
+ const { columnsDefinition, entityDefinition } = getEntityDefinition(
+ new (entityType as ObjectType)(),
+ );
+ const entities: EntityType[] = [];
+ results.rows.forEach(row => {
+ const entity = new (entityType as ObjectType)();
+ Object.keys(row).forEach(key => {
+ if (columnsDefinition[key]) {
+ entity[columnsDefinition[key].nodename] = this.dataTransformer.fromDbString(
+ row[key],
+ columnsDefinition[key].type,
+ );
+ }
+ });
+ entities.push(entity);
+ });
+
+ const nextPageToken = options?.pagination?.page_token || "0";
+ const limit = parseInt(options?.pagination?.limitStr);
+ const nextToken = entities.length === limit && (parseInt(nextPageToken) + limit).toString(10);
+ const nextPage: Paginable = new Pagination(nextToken, options?.pagination?.limitStr || "100");
+ logger.debug(
+ `services.database.orm.postgres.find - Query Result (items=${entities.length}): ${query}`,
+ );
+
+ return new ListResult(entityDefinition.type, entities, nextPage);
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ remove(entities: Entity[]): Promise {
+ return Promise.all(entities.map(entity => this.removeOne(entity)));
+ }
+
+ async removeOne(entity: Entity): Promise {
+ const query = this.queryBuilder.buildDelete(entity);
+ logger.debug(`service.database.orm.postgres.remove - Query: "${query}"`);
+ const result = await this.client.query(query[0] as string, query[1] as never[]);
+ return result.rowCount > 0;
+ }
+
+ async upsert(entities: Entity[], _options: UpsertOptions): Promise {
+ if (!_options?.action) {
+ throw new Error("Can't perform unknown operation");
+ } else {
+ return Promise.all(entities.map(entity => this.upsertOne(entity, _options)));
+ }
+ }
+
+ private async upsertOne(entity: Entity, _options: UpsertOptions): Promise {
+ if (_options.action == "INSERT") {
+ const query = this.queryBuilder.buildInsert(entity);
+ return this.execute(query);
+ } else if (_options.action == "UPDATE") {
+ if (!(await this.execute(this.queryBuilder.buildUpdate(entity)))) {
+ return this.execute(this.queryBuilder.buildInsert(entity));
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private async execute(query: Query) {
+ logger.debug(`service.database.orm.postgres - Query: "${query[0]}"`);
+ try {
+ const result: QueryResult = await this.client.query(query[0] as string, query[1] as never[]);
+ return result.rowCount > 0;
+ } catch (err) {
+ logger.error({ err }, `services.database.orm.postgres - Error with SQL query: ${query[0]}`);
+ return false;
+ }
+ }
+
+ async getTableDefinition(name: string): Promise {
+ try {
+ const query = `SELECT
+ table_name,
+ column_name,
+ data_type
+ FROM
+ information_schema.columns
+ WHERE
+ table_name = $1`;
+ const dbResult: QueryResult = await this.client.query(query, [name]);
+ return dbResult.rows.map(row => row.column_name);
+ } catch (err) {
+ this.logger.warn("Error querying table information", err);
+ throw err;
+ }
+ }
+}
+
+export type TableRowInfo = {
+ table_name: string;
+ column_name: string;
+ data_type: string;
+};
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/manager.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/manager.ts
index 42a5a40a..343720e9 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/manager.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/manager.ts
@@ -9,12 +9,16 @@ import { DatabaseEntitiesRemovedEvent, DatabaseEntitiesSavedEvent } from "./type
import { localEventBus } from "../../../../framework/event-bus";
export default class EntityManager> {
- private toPersist: EntityType[] = [];
+ private toInsert: EntityType[] = [];
+ private toUpdate: EntityType[] = [];
private toRemove: EntityType[] = [];
constructor(readonly connector: Connector) {}
public persist(entity: any): this {
+ logger.trace(
+ `services.database.orm.entity-manager.persist - entity: ${JSON.stringify(entity)}`,
+ );
if (!entity.constructor.prototype._entity || !entity.constructor.prototype._columns) {
logger.error("Can not persist this object %o", entity);
throw Error("Cannot persist this object: it is not an entity.");
@@ -24,32 +28,7 @@ export default class EntityManager> {
const { columnsDefinition, entityDefinition } = getEntityDefinition(entity);
const primaryKey: string[] = unwrapPrimarykey(entityDefinition);
- primaryKey.forEach(pk => {
- if (entity[pk] === undefined) {
- const definition = columnsDefinition[pk];
-
- if (!definition) {
- throw Error(`There is no definition for primary key ${pk}`);
- }
-
- //Create default value
- switch (definition.options.generator || definition.type) {
- case "uuid":
- entity[pk] = uuidv4();
- break;
- case "timeuuid":
- entity[pk] = uuidv1();
- break;
- case "number":
- entity[pk] = 0;
- break;
- default:
- entity[pk] = "";
- }
- }
- });
-
- //Apply the onUpsert
+ //apply on upsert(generating created_at, updated_at fields)
for (const column in columnsDefinition) {
const definition = columnsDefinition[column];
if (definition.options.onUpsert) {
@@ -57,8 +36,39 @@ export default class EntityManager> {
}
}
- this.toPersist = this.toPersist.filter(e => e !== entity);
- this.toPersist.push(_.cloneDeep(entity));
+ // generate primary key
+ const emptyPkFields = primaryKey.filter(
+ pk => entity[columnsDefinition[pk].nodename] === undefined,
+ );
+ emptyPkFields.forEach(pk => {
+ const definition = columnsDefinition[pk];
+
+ if (!definition) {
+ throw Error(`There is no definition for primary key ${pk}`);
+ }
+ //Create default value
+ switch (definition.options.generator || definition.type) {
+ case "uuid":
+ entity[columnsDefinition[pk].nodename] = uuidv4();
+ break;
+ case "timeuuid":
+ entity[columnsDefinition[pk].nodename] = uuidv1();
+ break;
+ case "number":
+ entity[columnsDefinition[pk].nodename] = 0;
+ break;
+ default:
+ entity[columnsDefinition[pk].nodename] = "";
+ }
+ });
+
+ if (emptyPkFields.length > 0) {
+ this.toInsert = this.toInsert.filter(e => e !== entity);
+ this.toInsert.push(_.cloneDeep(entity));
+ } else {
+ this.toUpdate = this.toUpdate.filter(e => e !== entity);
+ this.toUpdate.push(_.cloneDeep(entity));
+ }
return this;
}
@@ -78,25 +88,38 @@ export default class EntityManager> {
}
public async flush(): Promise {
- this.toPersist = _.uniqWith(this.toPersist, _.isEqual);
+ this.toInsert = _.uniqWith(this.toInsert, _.isEqual);
+ this.toUpdate = _.uniqWith(this.toUpdate, _.isEqual);
this.toRemove = _.uniqWith(this.toRemove, _.isEqual);
localEventBus.publish("database:entities:saved", {
- entities: this.toPersist.map(e => _.cloneDeep(e)),
+ entities: this.toInsert.map(e => _.cloneDeep(e)),
+ } as DatabaseEntitiesSavedEvent);
+
+ localEventBus.publish("database:entities:saved", {
+ entities: this.toUpdate.map(e => _.cloneDeep(e)),
} as DatabaseEntitiesSavedEvent);
localEventBus.publish("database:entities:saved", {
entities: this.toRemove.map(e => _.cloneDeep(e)),
} as DatabaseEntitiesRemovedEvent);
- await this.connector.upsert(this.toPersist);
- await this.connector.remove(this.toRemove);
+ if (this.toInsert.length > 0) {
+ await this.connector.upsert(this.toInsert, { action: "INSERT" });
+ }
+ if (this.toUpdate.length > 0) {
+ await this.connector.upsert(this.toUpdate, { action: "UPDATE" });
+ }
+ if (this.toRemove.length > 0) {
+ await this.connector.remove(this.toRemove);
+ }
return this;
}
public reset(): void {
- this.toPersist = [];
+ this.toInsert = [];
+ this.toUpdate = [];
this.toRemove = [];
}
}
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
index 374fcef1..2d6f64c1 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/repository/repository.ts
@@ -96,18 +96,20 @@ export default class Repository {
}
async save(entity: EntityType, _context?: ExecutionContext): Promise {
- (await this.manager.persist(entity).flush()).reset();
+ this.manager.persist(entity);
+ return this.manager.flush().then(manager => manager.reset());
}
async saveAll(entities: EntityType[] = [], _context?: ExecutionContext): Promise {
logger.debug("services.database.repository - Saving entities");
entities.forEach(entity => this.manager.persist(entity));
- await (await this.manager.flush()).reset();
+ return this.manager.flush().then(manager => manager.reset());
}
async remove(entity: EntityType, _context?: ExecutionContext): Promise {
- await (await this.manager.remove(entity).flush()).reset();
+ this.manager.remove(entity);
+ return this.manager.flush().then(manager => manager.reset());
}
//Avoid using this except when no choice
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/types.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/types.ts
index 3d04efad..dbf14401 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/types.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/types.ts
@@ -46,7 +46,7 @@ export type ColumnType =
export type EntityTarget = ObjectType;
// eslint-disable-next-line @typescript-eslint/ban-types
-export type ObjectType = { new (): T } | Function;
+export type ObjectType = { new (): T };
/** Local Event bus */
diff --git a/tdrive/backend/node/src/core/platform/services/database/services/orm/utils.ts b/tdrive/backend/node/src/core/platform/services/database/services/orm/utils.ts
index 22f51b94..1f8bf8c5 100644
--- a/tdrive/backend/node/src/core/platform/services/database/services/orm/utils.ts
+++ b/tdrive/backend/node/src/core/platform/services/database/services/orm/utils.ts
@@ -6,10 +6,10 @@ export function getEntityDefinition(instance: any): {
entityDefinition: EntityDefinition;
columnsDefinition: { [name: string]: ColumnDefinition };
} {
- const entityConfituration = _.cloneDeep(instance.constructor.prototype._entity);
+ const entityConfiguration = _.cloneDeep(instance.constructor.prototype._entity);
const entityColumns = _.cloneDeep(instance.constructor.prototype._columns);
return {
- entityDefinition: entityConfituration,
+ entityDefinition: entityConfiguration,
columnsDefinition: entityColumns,
};
}
@@ -17,11 +17,10 @@ export function getEntityDefinition(instance: any): {
export function unwrapPrimarykey(entityDefinition: EntityDefinition): string[] {
const initial = [...entityDefinition.options.primaryKey];
const partitionKey = initial.shift();
- const primaryKey: string[] = [
+ return [
...(typeof partitionKey === "string" ? [partitionKey] : partitionKey),
...(initial as string[]),
];
- return primaryKey;
}
export function unwrapIndexes(entityDefinition: EntityDefinition): string[] {
@@ -89,8 +88,7 @@ export function fromMongoDbOrderable(orderable: string): string {
return null;
}
const uuid_arr = orderable.split("-");
- const timeuuid = [uuid_arr[2], uuid_arr[1], uuid_arr[0], uuid_arr[3], uuid_arr[4]].join("-");
- return timeuuid;
+ return [uuid_arr[2], uuid_arr[1], uuid_arr[0], uuid_arr[3], uuid_arr[4]].join("-");
}
/**
@@ -103,8 +101,7 @@ export function toMongoDbOrderable(timeuuid?: string): string {
return null;
}
const uuid_arr = timeuuid.split("-");
- const time_str = [uuid_arr[2], uuid_arr[1], uuid_arr[0], uuid_arr[3], uuid_arr[4]].join("-");
- return time_str;
+ return [uuid_arr[2], uuid_arr[1], uuid_arr[0], uuid_arr[3], uuid_arr[4]].join("-");
}
/**
diff --git a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts
index 85b03ecd..53672bb8 100644
--- a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts
+++ b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts
@@ -4,6 +4,7 @@ import p from "path";
import { rm } from "fs/promises"; // Do not change the import, this is not the same function import { rm } from "fs"
import { StorageConnectorAPI, WriteMetadata } from "../../provider";
import fs from "fs";
+import { logger } from "../../../../framework/logger";
export type LocalConfiguration = {
path: string;
@@ -18,6 +19,7 @@ export default class LocalConnectorService implements StorageConnectorAPI {
write(relativePath: string, stream: Readable): Promise {
const path = this.getFullPath(relativePath);
+ logger.trace(`Writing file ${path}`);
const directory = p.dirname(path);
if (!existsSync(directory)) {
@@ -30,10 +32,12 @@ export default class LocalConnectorService implements StorageConnectorAPI {
const file = createWriteStream(path);
file
.on("error", function (err) {
+ logger.error(`Error ${err.message} during writing file ${path}`);
reject(err);
})
.on("finish", () => {
const stats = statSync(path);
+ logger.trace(`File ${path} have been written`);
resolve({
size: stats.size,
});
@@ -43,6 +47,7 @@ export default class LocalConnectorService implements StorageConnectorAPI {
}
async read(path: string): Promise {
+ logger.trace(`Reading file ... ${path}`);
const fullPath = this.getFullPath(path);
if (!fs.existsSync(fullPath)) {
throw new Error(`File doesn't exists ${fullPath}`);
diff --git a/tdrive/backend/node/src/core/platform/services/storage/index.ts b/tdrive/backend/node/src/core/platform/services/storage/index.ts
index 0d9d30d9..f4afefed 100644
--- a/tdrive/backend/node/src/core/platform/services/storage/index.ts
+++ b/tdrive/backend/node/src/core/platform/services/storage/index.ts
@@ -51,7 +51,8 @@ export default class StorageService extends TdriveService implements
} else {
logger.info("Using 'local' connector for storage.");
const defaultHomeDir = this.configuration.get("local.path");
- if (defaultHomeDir) this.homeDir = `/${defaultHomeDir}`;
+ if (defaultHomeDir) this.homeDir = `${defaultHomeDir}`;
+ logger.trace(`Home directory for the storage: ${this.homeDir}`);
}
logger.info(
`Using 'local' connector for storage${
diff --git a/tdrive/backend/node/src/services/applications-api/index.ts b/tdrive/backend/node/src/services/applications-api/index.ts
index 33da4733..cd0ce72e 100644
--- a/tdrive/backend/node/src/services/applications-api/index.ts
+++ b/tdrive/backend/node/src/services/applications-api/index.ts
@@ -5,6 +5,7 @@ import { Prefix, TdriveService } from "../../core/platform/framework";
import WebServerAPI from "../../core/platform/services/webserver/provider";
import Application from "../applications/entities/application";
import web from "./web/index";
+import { logger } from "../../core/platform/framework/logger";
@Prefix("/api")
export default class ApplicationsApiService extends TdriveService {
@@ -26,7 +27,7 @@ export default class ApplicationsApiService extends TdriveService {
if (domain && prefix) {
try {
fastify.all("/" + prefix + "/*", async (req, rep) => {
- console.log("Proxying", req.method, req.url, "to", domain);
+ logger.info("Proxying", req.method, req.url, "to", domain);
try {
const response = await axios.request({
url: domain + req.url,
@@ -54,15 +55,15 @@ export default class ApplicationsApiService extends TdriveService {
rep.send(response.data);
} catch (err) {
- console.error(`${err}`);
+ logger.error(`${err}`);
rep.raw.statusCode = 500;
rep.raw.end(JSON.stringify({ error: err.message }));
}
});
- console.log("Listening at ", "/" + prefix + "/*");
+ logger.info("Listening at ", "/" + prefix + "/*");
} catch (e) {
- console.log(e);
- console.log("Can't listen to ", "/" + prefix + "/*");
+ logger.error(e);
+ logger.info("Can't listen to ", "/" + prefix + "/*");
}
}
}
diff --git a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts
index 9034d88b..a8b9cb7e 100644
--- a/tdrive/backend/node/src/services/documents/web/controllers/documents.ts
+++ b/tdrive/backend/node/src/services/documents/web/controllers/documents.ts
@@ -25,8 +25,6 @@ import {
} from "../../types";
import { DriveFileDTO } from "../dto/drive-file-dto";
import { DriveFileDTOBuilder } from "../../services/drive-file-dto-builder";
-import { ExecutionContext } from "../../../../core/platform/framework/api/crud-service";
-import gr from "../../../global-resolver";
import config from "config";
export class DocumentsController {
@@ -35,12 +33,6 @@ export class DocumentsController {
? config.get("drive.rootAdmins")
: [];
- private getCompanyUserRole(companyId: string, userId: string, context?: ExecutionContext) {
- return gr.services.companies
- .getCompanyUser({ id: companyId }, { id: userId }, context)
- .then(a => (a ? a.level : null));
- }
-
/**
* Creates a DriveFile item
*
diff --git a/tdrive/backend/node/src/services/user/entities/company_user.ts b/tdrive/backend/node/src/services/user/entities/company_user.ts
index 2e2264d0..c43f1a86 100644
--- a/tdrive/backend/node/src/services/user/entities/company_user.ts
+++ b/tdrive/backend/node/src/services/user/entities/company_user.ts
@@ -35,22 +35,6 @@ export default class CompanyUser {
@Column("last_update_day", "number")
lastUpdateDay: number;
-
- /**
- * 0: member,
- * 1, 2, 3: admin,
- */
- @Column("level", "number")
- level: number; //Depreciated
-
- @Column("is_externe", "tdrive_boolean")
- isExterne: boolean; //Depreciated
-
- @Column("did_connect_today", "tdrive_boolean")
- didConnectToday: boolean; //Depreciated
-
- @Column("app_used_today", "json")
- appUsedToday: Array; //Depreciated
}
export type CompanyUserPrimaryKey = Partial>;
diff --git a/tdrive/backend/node/src/services/user/services/companies.ts b/tdrive/backend/node/src/services/user/services/companies.ts
index 585ea2d7..f4265c5f 100644
--- a/tdrive/backend/node/src/services/user/services/companies.ts
+++ b/tdrive/backend/node/src/services/user/services/companies.ts
@@ -276,9 +276,6 @@ export class CompanyServiceImpl {
}
entity.role = role;
- if (level) {
- entity.level = level;
- }
entity.applications = applications;
await this.companyUserRepository.save(entity, context);
diff --git a/tdrive/backend/node/src/services/workspaces/entities/workspace_user.ts b/tdrive/backend/node/src/services/workspaces/entities/workspace_user.ts
index 00258975..947b0535 100644
--- a/tdrive/backend/node/src/services/workspaces/entities/workspace_user.ts
+++ b/tdrive/backend/node/src/services/workspaces/entities/workspace_user.ts
@@ -29,9 +29,6 @@ export default class WorkspaceUser {
@Column("last_access", "number")
lastAccess: number;
-
- @Column("is_externe", "tdrive_boolean")
- isExternal: boolean; //Depreciated
}
export type WorkspaceUserPrimaryKey = Partial>;
diff --git a/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts b/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts
index a6edb0fc..3e40e63d 100644
--- a/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts
+++ b/tdrive/backend/node/test/e2e/common/entities/mock_entities.ts
@@ -25,7 +25,7 @@ export class DriveFileMockClass {
extension: string;
description: string;
tags: string[];
- last_modified: string;
+ last_modified: number;
access_info: MockAccessInformation;
creator: string;
is_directory: boolean;
diff --git a/tdrive/backend/node/test/e2e/users/users.search.spec.ts b/tdrive/backend/node/test/e2e/users/users.search.spec.ts
index 491772ad..a4cde316 100644
--- a/tdrive/backend/node/test/e2e/users/users.search.spec.ts
+++ b/tdrive/backend/node/test/e2e/users/users.search.spec.ts
@@ -81,7 +81,7 @@ describe("The /users API", () => {
//Wait for indexation to happen
await new Promise(r => setTimeout(r, 5000));
- let resources = await search("ha");
+ let resources = await search("ha", platform.workspace.company_id);
expect(resources.length).toBe(2);
resources = await search("bob rabiot");
diff --git a/tdrive/backend/node/test/e2e/workspaces/workspace-users.spec.ts b/tdrive/backend/node/test/e2e/workspaces/workspace-users.spec.ts
index 08edb018..dc6d5c21 100644
--- a/tdrive/backend/node/test/e2e/workspaces/workspace-users.spec.ts
+++ b/tdrive/backend/node/test/e2e/workspaces/workspace-users.spec.ts
@@ -285,10 +285,6 @@ describe("The /workspace users API", () => {
expect(response.statusCode).toBe(201);
const resource = response.json()["resource"];
checkUserObject(resource);
-
- workspaceUsersCount = await testDbService.getWorkspaceUsersCountFromDb(workspaceId);
- expect(workspaceUsersCount).toBe(5);
-
});
});
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-data-transform.test.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-data-transform.test.ts
new file mode 100644
index 00000000..e3d794b2
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-data-transform.test.ts
@@ -0,0 +1,173 @@
+
+import {
+ PostgresDataTransformer, TypeMappings
+} from "../../../../../../../../../src/core/platform/services/database/services/orm/connectors/postgres/postgres-data-transform";
+import { randomInt, randomUUID } from "crypto";
+import { ColumnType } from "../../../../../../../../../src/core/platform/services/database/services/orm/types";
+import { v4 } from "uuid";
+import { DriveScope } from "../../../../../../../../../src/services/documents/entities/drive-file";
+
+describe('The PostgresDataTransformer', () => {
+
+ test('toDbString anything to counter, blob and other not implemented types should throw an error', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(() => subj.toDbString(randomInt(1000), "blob")).toThrow(Error);
+ expect(() => subj.toDbString(null, "blob")).toThrow(Error);
+ });
+
+ test.each(Object.keys(TypeMappings))('toDbString for "%s" and null or undefined --> to null', async (type: ColumnType) => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString(null, type)).toBeNull();
+ expect(subj.toDbString(undefined, type)).toBeNull();
+ });
+
+ test('toDbString for tdrive_int, tdrive_datetime should return the same number', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+ const number = randomInt(1000);
+
+ //then
+ expect(subj.toDbString(number, "number")).toBe(number);
+ expect(subj.toDbString(number, "counter")).toBe(number);
+ expect(subj.toDbString(number, "tdrive_datetime")).toBe(number);
+ expect(subj.toDbString(String(number), "tdrive_datetime")).toBe(number);
+ });
+
+ test('toDbString for tdrive_int, tdrive_datetime should throw error if its not a number', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(() => subj.toDbString("", "number")).toThrow(Error);
+ expect(() => subj.toDbString("str_" + randomUUID(), "tdrive_datetime")).toThrow(Error);
+ expect(() => subj.toDbString(true, "tdrive_datetime")).toThrow(Error);
+ expect(() => subj.toDbString({ idx: 1} , "tdrive_datetime")).toThrow(Error);
+ });
+
+ test('toDbString for uuid and timeuuid should throw exception if value is not string', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(() => subj.toDbString({ }, "uuid")).toThrow(Error);
+ expect(() => subj.toDbString({ }, "timeuuid")).toThrow(Error);
+ expect(() => subj.toDbString(true, "uuid")).toThrow(Error);
+ expect(() => subj.toDbString(1 , "timeuuid")).toThrow(Error);
+ });
+
+ test('toDbString for uuid and timeuuid should be transformed to the same string', async () => {
+ //given
+ const uuid = v4()
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString(uuid, "uuid")).toBe(uuid);
+ expect(subj.toDbString(uuid, "timeuuid")).toBe(uuid);
+ });
+
+
+
+ test('toDbString for boolean and to tdrive_boolean should return boolean', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+ const number = randomInt(1000) + 1;
+
+ //then
+ expect(subj.toDbString(number, "tdrive_boolean")).toBe(true);
+ expect(subj.toDbString(number, "boolean")).toBe(true);
+ expect(subj.toDbString(0, "tdrive_boolean")).toBe(false);
+ expect(subj.toDbString(-number, "boolean")).toBe(true);
+ expect(subj.toDbString(true, "boolean")).toBe(true);
+ expect(subj.toDbString(false, "boolean")).toBe(false);
+ });
+
+ test('toDbString for boolean and to tdrive_boolean should throw error for any not boolean', async () => {
+ //given
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(() => subj.toDbString("true", "tdrive_boolean")).toThrow(Error);
+ expect(() => subj.toDbString("true", "boolean")).toThrow(Error);
+ expect(() => subj.toDbString("false", "tdrive_boolean")).toThrow(Error);
+ expect(() => subj.toDbString("false", "boolean")).toThrow(Error);
+ expect(() => subj.toDbString({ }, "boolean")).toThrow(Error);
+ expect(() => subj.toDbString([true], "tdrive_boolean")).toThrow(Error);
+ });
+
+
+ test('toDbString for string should return boolean the result of toString() method', async () => {
+ //given
+ const scope: DriveScope = "personal";
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString("test_string", "string")).toBe("test_string");
+ expect(subj.toDbString(scope, "string")).toBe("personal");
+ });
+
+ test('toDbString for json should return stringified object', async () => {
+ //given
+ const file = { id: "123", amount: 10};
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString(file, "json")).toBe('{"id":"123","amount":10}');
+ expect(subj.toDbString(10, "json")).toBe("10");
+ });
+
+ test('toDbString for encoded_string and empty secret should return plain string', async () => {
+ //given
+ const str = randomUUID();
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString(str, "encoded_string")).toBe(str);
+ });
+
+ test('toDbString for encoded_json and empty secret should return plain string', async () => {
+ //then
+ //given
+ const file = { id: "123", amount: 10};
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "" });
+
+ //then
+ expect(subj.toDbString(file, "encoded_json")).toBe('{"id":"123","amount":10}');
+ expect(subj.toDbString(10, "encoded_json")).toBe("10");
+ });
+
+ test('toDbString for encoded_json should return decodable string different from the source one', async () => {
+ //then
+ //given
+ const file = { id: "123", amount: 10};
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "hey it's secret" });
+
+ //then
+ const encodedJson = subj.toDbString(file, "encoded_json");
+ expect(encodedJson).not.toBe('{"id":"123","amount":10}');
+ const encodedNumber = subj.toDbString(10, "encoded_json");
+ expect(encodedNumber).not.toBe("10");
+
+ expect(subj.fromDbString(encodedJson, "encoded_json")).toEqual(file);
+ expect(subj.fromDbString(encodedNumber, "encoded_json")).toEqual(10);
+ });
+
+ test('toDbString for encoded_string should return decodable string different from the source one', async () => {
+ //then
+ //given
+ const str = randomUUID();
+ const subj: PostgresDataTransformer = new PostgresDataTransformer({ secret: "hey it's secret" });
+
+ //then
+ const encodedStr = subj.toDbString(str, "encoded_json");
+ expect(encodedStr).not.toBe('{"id":"123","amount":10}');
+
+ expect(subj.fromDbString(encodedStr, "encoded_json")).toEqual(str);
+ });
+
+});
\ No newline at end of file
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-query-builder.test.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-query-builder.test.ts
new file mode 100644
index 00000000..7e9a38f1
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres-query-builder.test.ts
@@ -0,0 +1,149 @@
+import {
+ PostgresQueryBuilder
+} from "../../../../../../../../../src/core/platform/services/database/services/orm/connectors/postgres/postgres-query-builder";
+import { expect, jest } from "@jest/globals";
+import { randomInt, randomUUID } from "crypto";
+import { newTestDbEntity, normalizeWhitespace, TestDbEntity } from "./utils";
+import {
+ comparisonType
+} from "../../../../../../../../../src/core/platform/services/database/services/orm/repository/repository";
+
+describe('The PostgresQueryBuilder', () => {
+
+ const subj: PostgresQueryBuilder = new PostgresQueryBuilder("");
+
+ beforeEach(async () => {
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('buildSelect query with filter', async () => {
+ //given
+ const filter = new TestDbEntity({id: randomUUID(), company_id: randomUUID() })
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, filter as { [key: string]: any }, null);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE id = $1 AND company_id = $2 ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual([filter.id, filter.company_id]);
+ });
+
+ test('buildSelect query with array in filters', async () => {
+ //given
+ const filter = {id: randomUUID(), company_id: [randomUUID(), randomUUID()] };
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, filter as { [key: string]: any }, null);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE id = $1 AND company_id IN ($2,$3) ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual([filter.id, filter.company_id[0], filter.company_id[1]]);
+ });
+
+ test('buildSelect query "lt" options', async () => {
+ //given
+ const options = { $lt: [["added", randomInt(10)] as comparisonType, ["added", randomInt(10)] as comparisonType]};
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, null, options);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE added < $1 AND added < $2 ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual(options.$lt.map(e=> e[1]));
+ });
+
+ test('buildSelect query "gt" options', async () => {
+ //given
+ const options = { $gt: [["added", randomInt(10)] as comparisonType, ["added", randomInt(10)] as comparisonType]};
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, null, options);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE added > $1 AND added > $2 ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual(options.$gt.map(e=> e[1]));
+ });
+
+ test('buildSelect query "in" options', async () => {
+ //given
+ const options = { $in: [["added", [randomInt(10), randomInt(10)]] as comparisonType,]};
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, null, options);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE added IN ($1,$2) ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual(options.$in[0][1]);
+ });
+
+ test('buildSelect query "like" options', async () => {
+ //given
+ const options = { $like: [["id", randomUUID()] as comparisonType, ["company_id", randomUUID()] as comparisonType]};
+
+ //when
+ const query = subj.buildSelect(TestDbEntity, null, options);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`SELECT * FROM "test_table" WHERE id LIKE $1 AND company_id LIKE $2 ORDER BY id DESC LIMIT 100 OFFSET 0`);
+ expect(query[1]).toEqual(options.$like.map(e=> `%${e[1]}%`));
+ });
+
+ test('buildDelete query', async () => {
+ //given
+ const entity = newTestDbEntity();
+
+ //when
+ const query = subj.buildDelete(entity);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`DELETE FROM "test_table" WHERE company_id = $1 AND id = $2`);
+ expect(query[1]).toEqual([entity.company_id, entity.id]);
+ });
+
+ test('buildInsert', async () => {
+ //given
+ const entity = newTestDbEntity();
+
+ //when
+ const query = subj.buildInsert(entity);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`INSERT INTO "test_table" (company_id, id, parent_id, is_in_trash, tags, added) VALUES ($1, $2, $3, $4, $5, $6)`);
+ assertInsertQueryParams(entity, query[1] as any[])
+ });
+
+ test('buildUpdate', async () => {
+ //given
+ const entity = newTestDbEntity();
+
+ //when
+ const query = subj.buildUpdate(entity);
+
+ //then
+ expect(normalizeWhitespace(query[0] as string)).toBe(`UPDATE "test_table" SET parent_id = $1, is_in_trash = $2, tags = $3, added = $4 WHERE company_id = $5 AND id = $6`);
+ assertUpdateQueryParams(entity, query[1] as any[])
+ });
+
+ const assertInsertQueryParams = (actual: TestDbEntity, expected: any[]) => {
+ expect(expected[0]).toBe(actual.company_id);
+ expect(expected[1]).toBe(actual.id);
+ expect(expected[2]).toBe(actual.parent_id);
+ expect(expected[3]).toBe(actual.is_in_trash);
+ expect(expected[4]).toBe( JSON.stringify(actual.tags));
+ expect(expected[5]).toBe(actual.added);
+ }
+
+ const assertUpdateQueryParams = (actual: TestDbEntity, expected: string[]) => {
+ expect(expected[0]).toBe(actual.parent_id);
+ expect(expected[1]).toBe(actual.is_in_trash);
+ expect(expected[2]).toBe( JSON.stringify(actual.tags));
+ expect(expected[3]).toBe(actual.added);
+ expect(expected[4]).toBe(actual.company_id);
+ expect(expected[5]).toBe(actual.id);
+ }
+
+
+});
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.debug.test.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.debug.test.ts
new file mode 100644
index 00000000..49e73d2f
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.debug.test.ts
@@ -0,0 +1,67 @@
+import 'reflect-metadata';
+
+import { DriveFile } from "../../../../../../../../../src/services/documents/entities/drive-file";
+import { describe, it } from '@jest/globals';
+import {
+ PostgresConnectionOptions,
+ PostgresConnector,
+} from '../../../../../../../../../src/core/platform/services/database/services/orm/connectors/postgres/postgres';
+import { getEntityDefinition } from '../../../../../../../../../src/core/platform/services/database/services/orm/utils';
+import { randomUUID } from "crypto";
+import { now } from "moment";
+
+//THIS IS NOT TEST, IT'S JUST RUNNER TO TEST POSTGRESQL API
+describe.skip('The Postgres Connector module', () => {
+
+ const subj: PostgresConnector = new PostgresConnector('postgres', {
+ user: "tdrive_user",
+ password: "tdrive_secret",
+ database: "tdrive",
+ port: 5432,
+ host: "localhost",
+ statement_timeout: 10000,
+ query_timeout: 10000
+
+ } as PostgresConnectionOptions, '');
+
+
+
+ it('createTable test ALTER TABLE query', async () => {
+ let driveFile = new DriveFile();
+ const definition = getEntityDefinition(driveFile);
+ await subj.connect();
+ //when
+ await subj.createTable(definition.entityDefinition, definition.columnsDefinition);
+
+ driveFile.id = randomUUID();
+ driveFile.name = "My Test File";
+ driveFile.company_id = randomUUID();
+ driveFile.access_info = {
+ entities: [],
+ public: {
+ token: randomUUID(),
+ password: randomUUID(),
+ expiration: now(),
+ level: "read",
+ }
+ }
+ driveFile.is_directory = false;
+ driveFile.is_in_trash = true;
+ driveFile.last_modified = now();
+ driveFile.tags = ["mytag", "ehey"]
+
+ await subj.upsert([driveFile], {action: "INSERT"} )
+
+ let files = await subj.find(DriveFile, null, null);
+ expect(files.getEntities().length).toBeGreaterThan(0);
+
+ await subj.remove(files.getEntities());
+
+ files = await subj.find(DriveFile, null, null);
+ expect(files.getEntities().length).toEqual(0);
+
+ await subj.drop();
+ //then
+ }, 3000000);
+
+});
\ No newline at end of file
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.test.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.test.ts
new file mode 100644
index 00000000..c51015ae
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/postgres.test.ts
@@ -0,0 +1,63 @@
+import 'reflect-metadata';
+
+import { describe, expect, jest } from '@jest/globals';
+import {
+ PostgresConnectionOptions,
+ PostgresConnector, TableRowInfo
+} from "../../../../../../../../../src/core/platform/services/database/services/orm/connectors/postgres/postgres";
+import { getEntityDefinition } from '../../../../../../../../../src/core/platform/services/database/services/orm/utils';
+import { TestDbEntity, normalizeWhitespace, newTestDbEntity } from "./utils";
+
+describe('The Postgres Connector module', () => {
+
+ const NUMBER_OF_HEALTHCHECK_CALLS = 1;
+
+ const subj: PostgresConnector = new PostgresConnector('postgres', {} as PostgresConnectionOptions, '');
+ let dbQuerySpy;
+
+ beforeEach(async () => {
+ dbQuerySpy = jest.spyOn((subj as any).client, 'query');
+ //healthcheck mock
+ dbQuerySpy
+ .mockReturnValueOnce({rows: [{now: 1}], rowCount: 1});
+ jest.spyOn((subj as any).client, 'connect').mockImplementation(jest.fn);
+ await subj.connect();
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('createTable generates table structure queries', async () => {
+ // given
+ const definition = getEntityDefinition(new TestDbEntity());
+ dbQuerySpy
+ //mock create table response
+ .mockReturnValueOnce({ rows: [], rowCount: 1})
+ //mock query for existing columns
+ .mockReturnValueOnce({ rows: [{ column_name: "parent_id"} as TableRowInfo, { column_name: "company_id"}], rowCount: 1 })
+ //mock alter table response
+ .mockReturnValueOnce({ rows: [], rowCount: 1 })
+ //mock alter table add primary key query response
+ .mockReturnValueOnce({ rows: [], rowCount: 1 })
+ //mock alter table create index query response
+ .mockReturnValueOnce({ rows: [], rowCount: 1 })
+ //mock alter table create index query response
+ .mockReturnValueOnce({ rows: [], rowCount: 1 })
+
+ //when
+ await subj.createTable(definition.entityDefinition, definition.columnsDefinition);
+
+ //then
+ expect(dbQuerySpy).toHaveBeenCalledTimes(6 + NUMBER_OF_HEALTHCHECK_CALLS);
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[1][0])).toBe(normalizeWhitespace(`CREATE TABLE IF NOT EXISTS "test_table" ( company_id UUID, id UUID, parent_id UUID, is_in_trash BOOLEAN, tags TEXT, added BIGINT );`))
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[2][0])).toBe(normalizeWhitespace("SELECT table_name, column_name, data_type FROM information_schema.columns WHERE table_name = $1"));
+ expect(dbQuerySpy.mock.calls[2][1]).toStrictEqual(["test_table"])
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[3][0])).toBe(`ALTER TABLE "test_table" ADD COLUMN id UUID, ADD COLUMN is_in_trash BOOLEAN, ADD COLUMN tags TEXT, ADD COLUMN added BIGINT`)
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[4][0])).toBe(`ALTER TABLE "test_table" ADD PRIMARY KEY ( company_id, id);`)
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[5][0])).toBe(`CREATE INDEX IF NOT EXISTS index_test_table_company_id_parent_id ON "test_table" ((company_id), parent_id)`)
+ expect(normalizeWhitespace(dbQuerySpy.mock.calls[6][0])).toBe(`CREATE INDEX IF NOT EXISTS index_test_table_company_id_is_in_trash ON "test_table" ((company_id), is_in_trash)`)
+ });
+});
+
+
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/utils.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/utils.ts
new file mode 100644
index 00000000..5d8aa4f0
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/connectors/postgres/utils.ts
@@ -0,0 +1,61 @@
+import { Column, Entity } from "../../../../../../../../../src/core/platform/services/database/services/orm/decorators";
+import { Type } from "class-transformer";
+import { randomInt, randomUUID } from "crypto";
+
+export const normalizeWhitespace = (query: string) => query.replace(/\s+/g, ' ').trim();
+
+@Entity("test_table", {
+ globalIndexes: [
+ ["company_id", "parent_id"],
+ ["company_id", "is_in_trash"],
+ ],
+ primaryKey: [["company_id"], "id"],
+ type: "test_table",
+})
+// @ts-ignore
+export class TestDbEntity {
+
+ @Type(() => String)
+ @Column("company_id", "uuid")
+ // @ts-ignore
+ company_id: string;
+
+ @Type(() => String)
+ @Column("id", "uuid", { generator: "uuid" })
+ // @ts-ignore
+ id: string;
+
+ @Type(() => String)
+ @Column("parent_id", "uuid")
+ // @ts-ignore
+ parent_id: string;
+
+ @Type(() => Boolean)
+ @Column("is_in_trash", "boolean")
+ // @ts-ignore
+ is_in_trash: boolean;
+
+ @Column("tags", "encoded_json")
+ // @ts-ignore
+ tags: string[];
+
+ @Type(() => Number)
+ @Column("added", "number")
+ // @ts-ignore
+ added: number;
+
+ public constructor(init?:Partial) {
+ Object.assign(this, init);
+ }
+}
+
+export const newTestDbEntity = () => {
+ return new TestDbEntity({
+ company_id: randomUUID(),
+ id: randomUUID(),
+ parent_id: randomUUID(),
+ is_in_trash: true,
+ tags: [randomUUID(), randomUUID()],
+ added: randomInt(1, 1000)
+ })
+}
\ No newline at end of file
diff --git a/tdrive/backend/node/test/unit/core/services/database/services/orm/manager.test.ts b/tdrive/backend/node/test/unit/core/services/database/services/orm/manager.test.ts
new file mode 100644
index 00000000..3b1cfaca
--- /dev/null
+++ b/tdrive/backend/node/test/unit/core/services/database/services/orm/manager.test.ts
@@ -0,0 +1,95 @@
+import 'reflect-metadata';
+
+import { describe, expect, it, jest } from '@jest/globals';
+import EntityManager from "../../../../../../../src/core/platform/services/database/services/orm/manager";
+import { Connector } from "../../../../../../../src/core/platform/services/database/services/orm/connectors";
+import { randomUUID } from "crypto";
+import { TestDbEntity } from "./connectors/postgres/utils";
+import WorkspaceUser, { getInstance } from "../../../../../../../src/services/workspaces/entities/workspace_user";
+
+describe('EntityManager', () => {
+
+ const subj: EntityManager = new EntityManager({ } as Connector);
+
+ beforeEach(async () => {
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ subj.reset();
+ });
+
+ test ("persist should store entity to insert if all fields for pk is empty", () => {
+ //when
+ subj.persist(new TestDbEntity());
+
+ //then
+ expect((subj as any).toInsert.length).toEqual(1);
+ expect((subj as any).toUpdate.length).toEqual(0);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toInsert[0].id).toBeDefined();
+ expect((subj as any).toInsert[0].company_id).toBeDefined();
+ });
+
+ test ("persist should store entity to insert if id is set", () => {
+ //when
+ subj.persist(new TestDbEntity({id: randomUUID()}));
+
+ //then
+ expect((subj as any).toInsert.length).toEqual(1);
+ expect((subj as any).toUpdate.length).toEqual(0);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toInsert[0].id).toBeDefined();
+ expect((subj as any).toInsert[0].company_id).toBeDefined();
+ });
+
+ test ("persist should store entity to insert if company_id is set", () => {
+ //when
+ subj.persist(new TestDbEntity({company_id: randomUUID()}));
+
+ //then
+ expect((subj as any).toInsert.length).toEqual(1);
+ expect((subj as any).toUpdate.length).toEqual(0);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toInsert[0].id).toBeDefined();
+ expect((subj as any).toInsert[0].company_id).toBeDefined();
+ });
+
+ test ("persist should store entity to update if all pk fields are set", () => {
+ //when
+ let entity = new TestDbEntity({id: randomUUID(), company_id: randomUUID()});
+ subj.persist(entity);
+
+ //then
+ expect((subj as any).toUpdate.length).toEqual(1);
+ expect((subj as any).toInsert.length).toEqual(0);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toUpdate[0]).toEqual(entity)
+ });
+
+ test ("persist should store entity to update if all pk fields are set and column name is different from field name", () => {
+ //when
+ let entity = getInstance({id: randomUUID(), workspaceId: randomUUID(), userId: randomUUID()});
+ subj.persist(entity);
+
+ //then
+ expect((subj as any).toUpdate.length).toEqual(1);
+ expect((subj as any).toInsert.length).toEqual(0);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toUpdate[0]).toEqual(entity)
+ });
+
+ test ("persist should store entity to insert if not all pk fields are set and column name is different from field name", () => {
+ //when
+ let entity = getInstance({id: randomUUID(), workspaceId: randomUUID()});
+ subj.persist(entity);
+
+ //then
+ expect((subj as any).toUpdate.length).toEqual(0);
+ expect((subj as any).toInsert.length).toEqual(1);
+ expect((subj as any).toRemove.length).toEqual(0);
+ expect((subj as any).toInsert[0]).toEqual(entity);
+ expect(entity.userId).toBeDefined();
+ });
+
+});
diff --git a/tdrive/docker-compose.dev.tests.opensearch.yml b/tdrive/docker-compose.dev.tests.opensearch.yml
index e349b7c4..ab8a3039 100644
--- a/tdrive/docker-compose.dev.tests.opensearch.yml
+++ b/tdrive/docker-compose.dev.tests.opensearch.yml
@@ -29,13 +29,15 @@ services:
# networks:
# - opensearch-net # All of the containers will join the same Docker bridge network
- mongo:
- container_name: mongo
- image: mongo
- volumes:
- - ./docker-data/mongo:/data/db
+ postgres:
+ image: postgres
+ restart: always
+ environment:
+ POSTGRES_DB: tdrive
+ POSTGRES_PASSWORD: tdrive_secret
+ POSTGRES_USER: tdrive_user
ports:
- - 27017:27017
+ - "5432:5432"
node:
image: tdrive/tdrive-node:test
@@ -48,7 +50,12 @@ services:
environment:
- LOG_LEVEL=error
- NODE_ENV=test
- - DB_DRIVER
+ - DB_DRIVER=postgres
+ - DB_POSTGRES_DBNAME=tdrive
+ - DB_POSTGRES_HOST=postgres
+ - DB_POSTGRES_PASSWORD=tdrive_secret
+ - DB_POSTGRES_PORT=5432
+ - DB_POSTGRES_USERNAME=tdrive_user
- PUBSUB_URLS=amqp://guest:guest@rabbitmq:5672
- SEARCH_OS_FLUSHINTERVAL=1
- SEARCH_OS_ENDPOINT=https://opensearch-node1:9200
@@ -56,8 +63,8 @@ services:
- SEARCH_OS_USE_AUTH=true
- SEARCH_OS_USERNAME=admin
depends_on:
- - mongo
+ - postgres
- opensearch-node1
links:
- - mongo
+ - postgres
- opensearch-node1
diff --git a/tdrive/docker-compose.tests.postgresql.yml b/tdrive/docker-compose.tests.postgresql.yml
new file mode 100644
index 00000000..5b53f32c
--- /dev/null
+++ b/tdrive/docker-compose.tests.postgresql.yml
@@ -0,0 +1,13 @@
+version: "3.4"
+
+services:
+
+ postgres:
+ image: postgres
+ restart: always
+ environment:
+ POSTGRES_DB: tdrive
+ POSTGRES_PASSWORD: tdrive_secret
+ POSTGRES_USER: tdrive_user
+ ports:
+ - "5432:5432"