From 2f0c24f64f8b35d19583e9bb9963d3030db19cef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:20:37 +0000 Subject: [PATCH 1/8] Bump elliptic from 6.5.5 to 6.5.7 in /app/assets Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.5 to 6.5.7. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.5...v6.5.7) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] --- app/assets/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/package-lock.json b/app/assets/package-lock.json index 6622b6589..39e84ae2e 100644 --- a/app/assets/package-lock.json +++ b/app/assets/package-lock.json @@ -12236,9 +12236,9 @@ "integrity": "sha512-4Nx0gP2tPNBLTrFxBMHpkQbtn2hidPVr/+/FTtcCiBYTucqc70zRyVZiOLj17Ui3wTO7SQ1/N+hkHYzJjBzt6A==" }, "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "dev": true, "dependencies": { "bn.js": "^4.11.9", From ce42192d6f15700328f808122f9b399e4dec10eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:20:45 +0000 Subject: [PATCH 2/8] Bump @babel/preset-env from 7.24.7 to 7.25.3 in /app/assets Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.24.7 to 7.25.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.25.3/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- app/assets/package-lock.json | 288 +++++++++++++++++++---------------- app/assets/package.json | 2 +- 2 files changed, 154 insertions(+), 136 deletions(-) diff --git a/app/assets/package-lock.json b/app/assets/package-lock.json index 6622b6589..63fb4cb7d 100644 --- a/app/assets/package-lock.json +++ b/app/assets/package-lock.json @@ -62,7 +62,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.17.3", "@babel/plugin-proposal-optional-chaining": "^7.16.7", "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.3", "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@babel/runtime": "^7.25.0", @@ -737,9 +737,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", @@ -794,26 +794,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -861,23 +849,23 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -887,14 +875,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -965,15 +953,14 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1020,13 +1007,28 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1036,12 +1038,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1068,13 +1070,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1458,15 +1460,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1508,12 +1510,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1556,18 +1558,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -1594,12 +1594,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1639,6 +1639,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1720,14 +1736,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1753,12 +1769,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1815,13 +1831,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" }, "engines": { @@ -1832,15 +1848,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1979,12 +1995,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -2217,12 +2233,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2313,19 +2329,20 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -2346,29 +2363,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -2377,7 +2395,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", @@ -2388,7 +2406,7 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", @@ -2397,7 +2415,7 @@ "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -11283,12 +11301,12 @@ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js." }, "node_modules/core-js-compat": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", - "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", + "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", "dev": true, "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", diff --git a/app/assets/package.json b/app/assets/package.json index 9c7be6a76..001c08655 100644 --- a/app/assets/package.json +++ b/app/assets/package.json @@ -72,7 +72,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.17.3", "@babel/plugin-proposal-optional-chaining": "^7.16.7", "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.3", "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@babel/runtime": "^7.25.0", From ba145a45f504861fbfa3573d36809666e0cda708 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:51:02 +0000 Subject: [PATCH 3/8] Bump version to 9.4.8 --- app/mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mix.exs b/app/mix.exs index baea800ec..7583b5f7c 100644 --- a/app/mix.exs +++ b/app/mix.exs @@ -3,7 +3,7 @@ Code.require_file("lib/env.ex") defmodule Meadow.MixProject do use Mix.Project - @app_version "9.4.7" + @app_version "9.4.8" def project do [ From 02b435a4fd50828b105a697e690e44ecb43ba5c9 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 19 Aug 2024 17:36:44 +0000 Subject: [PATCH 4/8] Automatically configure livebook S3 storage --- bin/meadow-livebook | 2 + .../deploy/modules/meadow_task/main.tf | 1 + .../deploy/task-definitions/meadow_app.json | 4 ++ infrastructure/deploy/variables.tf | 5 +++ livebook/Dockerfile | 3 +- .../{ => extensions}/meadow_livebook_auth.exs | 37 +++++++++++++++++++ 6 files changed, 51 insertions(+), 1 deletion(-) rename livebook/{ => extensions}/meadow_livebook_auth.exs (71%) diff --git a/bin/meadow-livebook b/bin/meadow-livebook index 0fa394475..de281ad35 100755 --- a/bin/meadow-livebook +++ b/bin/meadow-livebook @@ -15,11 +15,13 @@ echo -n " Starting Livebook..." docker buildx build -t nulib/meadow:livebook $ROOT/livebook > /dev/null 2>&1 LIVEBOOK_CONTAINER=$(docker run --rm -d --network host \ -v ${HOME}/.meadow_livebooks:/data/books \ + -v ${HOME}/environment/meadow_kino:/meadow_kino \ -e LB_MEADOW_COOKIE=${COOKIE} \ -e LB_MEADOW_NODE=meadow@${HOST} \ -e LIVEBOOK_NODE=livebook@${HOST} \ -e LIVEBOOK_DISTRIBUTION=name \ -e LIVEBOOK_COOKIE=${COOKIE} \ + -e MEADOW_LIVEBOOK_BUCKET=${MEADOW_LIVEBOOK_BUCKET} \ -e MEADOW_URL=https://${MEADOW_HOSTNAME}:3001/ \ nulib/meadow:livebook) https-proxy start 8082 8080 > /dev/null 2>&1 diff --git a/infrastructure/deploy/modules/meadow_task/main.tf b/infrastructure/deploy/modules/meadow_task/main.tf index dc144e3ec..21b78fb93 100644 --- a/infrastructure/deploy/modules/meadow_task/main.tf +++ b/infrastructure/deploy/modules/meadow_task/main.tf @@ -26,6 +26,7 @@ locals { db_queue_interval = var.db_queue_interval, db_queue_target = var.db_queue_target, docker_repository = data.aws_ecr_repository.meadow.repository_url, + livebook_bucket = var.livebook_shared_bucket, name = var.name, processes = var.meadow_processes } diff --git a/infrastructure/deploy/task-definitions/meadow_app.json b/infrastructure/deploy/task-definitions/meadow_app.json index 4e5f4033a..9710830ab 100644 --- a/infrastructure/deploy/task-definitions/meadow_app.json +++ b/infrastructure/deploy/task-definitions/meadow_app.json @@ -187,6 +187,10 @@ { "name": "LIVEBOOK_COOKIE", "value": "${secret_key_base}" + }, + { + "name": "MEADOW_LIVEBOOK_BUCKET", + "value": "${livebook_bucket}" } ], "mountPoints": [], diff --git a/infrastructure/deploy/variables.tf b/infrastructure/deploy/variables.tf index 665bb2ce8..e02fb4904 100644 --- a/infrastructure/deploy/variables.tf +++ b/infrastructure/deploy/variables.tf @@ -208,3 +208,8 @@ variable "trusted_referers" { type = string default = "" } + +variable "livebook_shared_bucket" { + type = string + default = "" +} diff --git a/livebook/Dockerfile b/livebook/Dockerfile index 46420a9cf..c0e47a1d6 100644 --- a/livebook/Dockerfile +++ b/livebook/Dockerfile @@ -7,4 +7,5 @@ ENV LIVEBOOK_IDENTITY_PROVIDER=custom:MeadowLivebookAuth ENV LIVEBOOK_IP=0.0.0.0 ENV LIVEBOOK_TOKEN_ENABLED=false RUN mkdir -p ${LIVEBOOK_DATA_PATH}/books -ADD ./meadow_livebook_auth.exs /app/user/extensions/meadow_livebook_auth.exs +RUN git clone https://github.com/nulib/meadow_kino.git /meadow_kino +ADD ./extensions/* /app/user/extensions/ diff --git a/livebook/meadow_livebook_auth.exs b/livebook/extensions/meadow_livebook_auth.exs similarity index 71% rename from livebook/meadow_livebook_auth.exs rename to livebook/extensions/meadow_livebook_auth.exs index 9cf63caca..a9d852ff5 100644 --- a/livebook/meadow_livebook_auth.exs +++ b/livebook/extensions/meadow_livebook_auth.exs @@ -23,6 +23,9 @@ defmodule MeadowLivebookAuth do @spec authenticate(GenServer.server(), Plug.Conn.t(), keyword()) :: {Plug.Conn.t(), map() | nil} def authenticate(server, conn, _) do + System.get_env("MEADOW_LIVEBOOK_BUCKET") + |> attach_storage() + with url <- find_meadow_url(server), user <- meadow_auth(url, conn) do {conn, user} @@ -90,4 +93,38 @@ defmodule MeadowLivebookAuth do map -> Map.put(map, key, value) end) end + + defp attach_storage(bucket) when is_binary(bucket) and byte_size(bucket) > 0 do + url = "https://s3.amazonaws.com/#{bucket}" + + Livebook.Hubs.get_file_systems() + |> Enum.any?(fn + %Livebook.FileSystem.S3{bucket_url: ^url} -> true + _ -> false + end) + |> attach_s3_storage(url) + end + + defp attach_storage(_), do: :noop + + defp attach_s3_storage(true, _), do: :noop + + defp attach_s3_storage(false, url) do + hash = :crypto.hash(:sha256, url) + encrypted_hash = "s3-" <> Base.url_encode64(hash, padding: false) + + [hub | _] = Livebook.Hubs.get_hubs() + + file_system = %Livebook.FileSystem.S3{ + id: encrypted_hash, + bucket_url: url, + external_id: nil, + region: Livebook.FileSystem.S3.region_from_url(url), + access_key_id: nil, + secret_access_key: nil, + hub_id: "personal-hub" + } + + Livebook.Hubs.create_file_system(hub, file_system) + end end From 7251d5a8e5bfd496311dff7078ee77b51822132e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:18:23 +0000 Subject: [PATCH 5/8] Bump gettext from 0.25.0 to 0.26.0 in /app Bumps [gettext](https://github.com/elixir-gettext/gettext) from 0.25.0 to 0.26.0. - [Changelog](https://github.com/elixir-gettext/gettext/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-gettext/gettext/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: gettext dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- app/mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mix.lock b/app/mix.lock index a472c7184..391622046 100644 --- a/app/mix.lock +++ b/app/mix.lock @@ -44,7 +44,7 @@ "faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.25.0", "98a95a862a94e2d55d24520dd79256a15c87ea75b49673a2e2f206e6ebc42e5d", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "38e5d754e66af37980a94fb93bb20dcde1d2361f664b0a19f01e87296634051f"}, + "gettext": {:hex, :gettext, "0.26.0", "f75fdb7d71beba746d15985fe084f8c1e2ddc4681ecb87839db263afdc994e48", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "9a201d32cc783eec5ecb7b26109d2805fa8d585a7e6a731d45eaa4e4c57cf41d"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "honeybadger": {:hex, :honeybadger, "0.21.0", "a81f1db7807c3a250f3c4e81c1baa76b59c27974dafe0ff61b69232346e05060", [:mix], [{:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:hackney, "~> 1.1", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.0.0 and < 2.0.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, ">= 1.0.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "444d7161e105ac2ee70e28c280cb3ad43d42f1d3d9670f2db21efffe9fbe6b0c"}, "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, From 4cf5fb019f99d8acb5d01708a13b30961db36147 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Mon, 19 Aug 2024 16:46:45 +0000 Subject: [PATCH 6/8] Make projects list searchable --- .../Dashboards/LocalAuthorities/List.jsx | 6 +- app/assets/js/components/Project/List.jsx | 344 +++++++++++------- app/assets/js/components/Project/List.test.js | 35 +- .../js/components/Project/ModalEdit.jsx | 96 +++++ .../js/components/Project/ModalEdit.test.jsx | 61 ++++ .../js/components/Project/project.gql.js | 14 + .../js/components/Project/project.gql.mock.js | 17 + app/assets/js/screens/Project/List.jsx | 6 +- app/assets/js/screens/Project/List.test.js | 4 - app/lib/meadow/ingest/projects.ex | 14 + app/lib/meadow_web/resolvers/ingest.ex | 4 + .../meadow_web/schema/types/ingest_types.ex | 7 + app/test/meadow/ingest/projects_test.exs | 6 + .../schema/query/projects_search_test.exs | 59 +++ 14 files changed, 514 insertions(+), 159 deletions(-) create mode 100644 app/assets/js/components/Project/ModalEdit.jsx create mode 100644 app/assets/js/components/Project/ModalEdit.test.jsx create mode 100644 app/test/meadow_web/schema/query/projects_search_test.exs diff --git a/app/assets/js/components/Dashboards/LocalAuthorities/List.jsx b/app/assets/js/components/Dashboards/LocalAuthorities/List.jsx index 124c7efcd..373ab06bf 100644 --- a/app/assets/js/components/Dashboards/LocalAuthorities/List.jsx +++ b/app/assets/js/components/Dashboards/LocalAuthorities/List.jsx @@ -8,7 +8,6 @@ import { IconEdit, IconImages, IconTrashCan } from "@js/components/Icon"; import { Link, useHistory } from "react-router-dom"; import { ModalDelete, SearchBarRow } from "@js/components/UI/UI"; import { - useApolloClient, useLazyQuery, useMutation, useQuery, @@ -23,7 +22,6 @@ import { toastWrapper } from "@js/services/helpers"; const colHeaders = ["Label", "Hint"]; export default function DashboardsLocalAuthoritiesList() { - const client = useApolloClient(); const history = useHistory(); const [currentAuthority, setCurrentAuthority] = React.useState(); const [filteredAuthorities, setFilteredAuthorities] = React.useState([]); @@ -78,7 +76,7 @@ export default function DashboardsLocalAuthoritiesList() { console.error("networkError", networkError); toastWrapper( "is-danger", - `Error in deleteNulAuthorityRecord GraphQL mutation` + `Error deleting authority record.` ); }, }); @@ -114,7 +112,7 @@ export default function DashboardsLocalAuthoritiesList() { console.error("networkError", networkError); toastWrapper( "is-danger", - `Error searching NUL local authorities through GraphQL LazyQuery` + `Error searching NUL local authorities.` ); }, }); diff --git a/app/assets/js/components/Project/List.jsx b/app/assets/js/components/Project/List.jsx index e050f5d8c..097a2c73f 100644 --- a/app/assets/js/components/Project/List.jsx +++ b/app/assets/js/components/Project/List.jsx @@ -1,174 +1,254 @@ -import React, { useState, useEffect } from "react"; -import { Link } from "react-router-dom"; -import { useMutation, useApolloClient } from "@apollo/client"; -import { DELETE_PROJECT, GET_PROJECTS } from "./project.gql.js"; -import UIModalDelete from "../UI/Modal/Delete"; +import { Button, Notification } from "@nulib/design-system"; +import React, { useState } from "react"; +import { useMutation, useQuery, useLazyQuery } from "@apollo/client"; +import { + DELETE_PROJECT, + GET_PROJECTS, + PROJECTS_SEARCH, + UPDATE_PROJECT, +} from "./project.gql.js"; +import { ModalDelete, SearchBarRow } from "@js/components/UI/UI"; import { formatDate, toastWrapper } from "@js/services/helpers"; import UIFormInput from "@js/components/UI/Form/Input"; -import { Button } from "@nulib/design-system"; import AuthDisplayAuthorized from "@js/components/Auth/DisplayAuthorized"; -import ProjectForm from "@js/components/Project/Form"; -import UISearchBarRow from "@js/components/UI/SearchBarRow"; +import ProjectsModalEdit from "@js/components/Project/ModalEdit"; import { IconEdit, IconTrashCan } from "@js/components/Icon"; +import { Link } from "react-router-dom"; + +const colHeaders = [ + "Project", + "S3 Bucket Folder", + "Ingest Sheets", + "Last Updated", + "Actions", +]; + +const ProjectList = () => { + const [currentProject, setCurrentProject] = useState(); + const [filteredProjects, setFilteredProjects] = useState([]); + const [searchValue, setSearchValue] = useState(""); + const [modalsState, setModalsState] = useState({ + delete: { + isOpen: false, + }, + update: { + isOpen: false, + }, + }); -const ProjectList = ({ projects }) => { - const [modalOpen, setModalOpen] = useState(false); - const [showForm, setShowForm] = useState(false); - const [activeProject, setActiveProject] = useState(); - const [activeModal, setActiveModal] = useState(); - const [projectList, setProjectList] = useState(); - const client = useApolloClient(); - const [deleteProject, { data }] = useMutation(DELETE_PROJECT, { + const { loading, error, data } = useQuery(GET_PROJECTS, { + pollInterval: 1000, + }); + + function filterValues() { + if (!data) return; + if (searchValue) { + projectsSearch({ + variables: { + query: searchValue, + }, + }); + } else { + setFilteredProjects([...data.projects]); + } + } + + const [ + deleteProject, + { error: deleteProjectError, loading: deleteProjectLoading }, + ] = useMutation(DELETE_PROJECT, { update(cache, { data: { deleteProject } }) { - const { projects } = client.readQuery({ query: GET_PROJECTS }); - const index = projects.findIndex( - (project) => project.id === deleteProject.id - ); - projects.splice(index, 1); - client.writeQuery({ - query: GET_PROJECTS, - data: { projects }, + cache.modify({ + fields: { + projects(existingProjects = [], { readField }) { + const newData = existingProjects.filter( + (projectRef) => deleteProject.id !== readField("id", projectRef), + ); + return [...newData]; + }, + }, }); - toastWrapper("is-success", `Project deleted successfully`); + }, + onError({ graphQLErrors, networkError }) { + console.error("graphQLErrors", graphQLErrors); + console.error("networkError", networkError); + toastWrapper("is-danger", `Error deleting project.`); }, }); - useEffect(() => { - projects && projects.length > 0 - ? setProjectList(projects) - : setProjectList([]); - }, [projects]); + const [updateProject, { error: updateError, loading: updateLoading }] = + useMutation(UPDATE_PROJECT, { + onCompleted({ updateProject }) { + toastWrapper("is-success", `Project: ${updateProject.title} updated`); + setCurrentProject(null); + filterValues(); + }, + }); - const onOpenModal = (e, project) => { - setActiveModal(project); - setModalOpen(true); - }; + const [ + projectsSearch, + { + error: errorProjectsSearch, + loading: loadingProjectsSearch, + data: dataProjectsSearch, + }, + ] = useLazyQuery(PROJECTS_SEARCH, { + fetchPolicy: "network-only", + onCompleted: (data) => { + setFilteredProjects([...data.projectsSearch]); + }, + onError({ graphQLErrors, networkError }) { + console.error("graphQLErrors", graphQLErrors); + console.error("networkError", networkError); + toastWrapper("is-danger", `Error searching projects.`); + }, + }); - const onEditProject = (project) => { - setActiveProject(project); - setShowForm(!showForm); - }; + React.useEffect(() => { + if (!data) return; + filterValues(); + }, [data, searchValue]); + + if (loading || deleteProjectLoading || updateLoading) return null; + if (error) return {error.toString()}; + if (deleteProjectError) + return ( + {deleteProjectError.toString()} + ); + if (updateError) + return {updateError.toString()}; - const onCloseModal = () => { - setActiveModal(null); - setModalOpen(false); - setActiveProject(null); + const handleConfirmDelete = () => { + deleteProject({ variables: { projectId: currentProject.id } }); + setCurrentProject(null); + setModalsState({ ...modalsState, delete: { isOpen: false } }); }; - const handleDeleteClick = () => { - setModalOpen(false); - if (activeModal.ingestSheets.length > 0) { - toastWrapper( - "is-danger", - `Project has existing ingest sheets. You must delete these before deleting project: ${activeModal.title} ` - ); - return setActiveModal(null); - } - deleteProject({ variables: { projectId: activeModal.id } }); - setActiveModal(null); + const handleDeleteClick = (project) => { + setCurrentProject({ ...project }); + setModalsState({ + ...modalsState, + delete: { isOpen: true }, + }); }; - const handleFilterChange = (e) => { - const filterValue = e.target.value.toUpperCase(); + const handleUpdate = (formData) => { + updateProject({ + variables: { + projectTitle: formData.title, + projectId: currentProject.id, + }, + }); + }; - if (!filterValue) { - return setProjectList(projects); - } - const filteredList = projectList.filter((project) => { - return project.title.toUpperCase().indexOf(filterValue) > -1; + const handleUpdateButtonClick = (project) => { + setCurrentProject({ ...project }); + setModalsState({ + ...modalsState, + update: { isOpen: true }, }); - setProjectList(filteredList); + }; + + const handleSearchChange = (e) => { + setSearchValue(e.target.value); }; return ( - <> - + + - +
- - - - - - - - + {colHeaders.map((col) => ( + + ))} - - {projectList && - projectList.map((project) => { - const { id, folder, title, updatedAt, ingestSheets } = project; - return ( - - - - - - - - - - ); - })} + + {filteredProjects.map((project) => { + const { id, folder, title, updatedAt, ingestSheets } = project; + + return ( + + + + + + + + ); + })}
All Projects
Projects3 Bucket Folder# Ingest SheetsLast UpdatedActions{col}
- - {title} - - {folder}{ingestSheets.length}{formatDate(updatedAt)} -
-

- -

-

- -

-
-
+ + {title} + + {folder}{ingestSheets.length}{formatDate(updatedAt)} +
+ + + + +
+
- + setModalsState({ + ...modalsState, + delete: { + isOpen: false, + }, + }) + } + handleConfirm={handleConfirmDelete} + thingToDeleteLabel={currentProject ? currentProject.title : ""} /> - + setModalsState({ + ...modalsState, + update: { + isOpen: false, + }, + }) + } + handleUpdate={handleUpdate} /> - + ); }; diff --git a/app/assets/js/components/Project/List.test.js b/app/assets/js/components/Project/List.test.js index 819393c71..18db4d05c 100644 --- a/app/assets/js/components/Project/List.test.js +++ b/app/assets/js/components/Project/List.test.js @@ -1,7 +1,7 @@ import React from "react"; import ProjectList from "./List"; import { renderWithRouterApollo } from "../../services/testing-helpers"; -import { getProjectsMock, mockProjects } from "./project.gql.mock"; +import { getProjectsMock, mockProjects, projectsSearchMock } from "./project.gql.mock"; import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { mockUser } from "@js/components/Auth/auth.gql.mock"; @@ -13,9 +13,9 @@ useIsAuthorized.mockReturnValue({ isAuthorized: () => true, }); -describe("BatchEditAboutDescriptiveMetadata component", () => { +describe("Project list component", () => { beforeEach(() => { - return renderWithRouterApollo(, { + return renderWithRouterApollo(, { mocks: [getProjectsMock], route: "/project/list", }); @@ -29,20 +29,27 @@ describe("BatchEditAboutDescriptiveMetadata component", () => { expect(await screen.findAllByTestId("project-title-row")).toHaveLength(2); }); - it("filters for a project by title", async () => { + it("opens delete modal", async () => { const user = userEvent.setup(); - const el = await screen.findByTestId("input-project-filter"); - expect(el); - expect(screen.getAllByTestId("project-title-row")).toHaveLength(2); - //filter for project title - await user.type(el, "Second"); - expect(screen.getAllByTestId("project-title-row")).toHaveLength(1); + expect(await screen.findAllByTestId("delete-button")).toHaveLength(2); + await user.click(screen.getAllByTestId("delete-button")[0]); + expect(screen.getAllByTestId("delete-modal")).toHaveLength(1); }); +}); - it("opens delete modal", async () => { +describe("ProjectList component searching", () => { + // TODO: Fix this. Why is is breaking out of nowhere? + xit("calls the GraphQL query successfully and renders results", async () => { + const dynamicMock = projectsSearchMock("f"); const user = userEvent.setup(); - expect(await screen.findAllByTestId("delete-button-row")).toHaveLength(2); - await user.click(screen.getAllByTestId("delete-button-row")[0]); - expect(screen.getAllByTestId("delete-modal")).toHaveLength(1); + + renderWithRouterApollo(, { + mocks: [getProjectsMock, dynamicMock], + }); + + const el = await screen.findByPlaceholderText("Search"); + expect(await screen.findAllByTestId("projects-row")).toHaveLength(2); + await user.type(el, "f"); + expect(await screen.findAllByText("fffff")); }); }); diff --git a/app/assets/js/components/Project/ModalEdit.jsx b/app/assets/js/components/Project/ModalEdit.jsx new file mode 100644 index 000000000..695a07722 --- /dev/null +++ b/app/assets/js/components/Project/ModalEdit.jsx @@ -0,0 +1,96 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Button } from "@nulib/design-system"; +import { useForm, FormProvider } from "react-hook-form"; +import UIFormInput from "@js/components/UI/Form/Input"; +import UIFormField from "@js/components/UI/Form/Field"; + +function ProjectsModalEdit({ + currentProject, + handleClose, + handleUpdate, + isOpen, +}) { + if (!currentProject) return null; + const [defaultValues, setDefaultValues] = React.useState({ + title: "", + }); + const methods = useForm(); + const { isDirty } = methods.formState; + + React.useEffect(() => { + setDefaultValues({ + title: currentProject.title, + }); + }, [currentProject]); + + const onSubmit = (data) => { + handleUpdate(data); + methods.reset(); + handleClose(); + }; + + return ( + +
+
+
+
+

Update Project

+ +
+
+ + + +
+
+ + +
+
+
+
+ ); +} + +ProjectsModalEdit.propTypes = { + currentProject: PropTypes.object, + handleClose: PropTypes.func, + handleUpdate: PropTypes.func, + isOpen: PropTypes.bool, +}; + +export default ProjectsModalEdit; diff --git a/app/assets/js/components/Project/ModalEdit.test.jsx b/app/assets/js/components/Project/ModalEdit.test.jsx new file mode 100644 index 000000000..d6729740a --- /dev/null +++ b/app/assets/js/components/Project/ModalEdit.test.jsx @@ -0,0 +1,61 @@ +import React from "react"; +import { render, screen, waitFor } from "@testing-library/react"; +import ProjectsModalEdit from "./ModalEdit"; +import userEvent from "@testing-library/user-event"; +import { mockProjects } from "./project.gql.mock"; + + +const submitCallback = jest.fn(); +const cancelCallback = jest.fn(); + +const props = { + currentProject: mockProjects[0], + isOpen: true, + handleUpdate: submitCallback, + handleClose: cancelCallback, +}; + +describe("ProjectsModalEdit component", () => { + beforeEach(() => { + render(); + }); + + it("renders the component", () => { + expect(screen.getByTestId("modal-project-update")); + }); + + it("renders all add form elements with default values", () => { + expect(screen.getByRole("form")); + expect(screen.getByLabelText("Title", { exact: false })).toHaveValue("Mock project title"); + expect(screen.getByTestId("submit-button")); + expect(screen.getByTestId("cancel-button")); + }); + + it("renders a disabled Submit button if no elements have been interacted with", async () => { + const user = userEvent.setup(); + + const submitButtonEl = screen.getByTestId("submit-button"); + expect(submitButtonEl).toBeDisabled(); + await user.type(screen.getByLabelText(/title/i), "something"); + expect(submitButtonEl).not.toBeDisabled(); + }); + + it("calls the Cancel and Submit callback functions", async () => { + const user = userEvent.setup(); + const expectedFormPostData = { + title: "foo bar", + }; + const labelEl = screen.getByLabelText(/title/i); + + await user.clear(labelEl); + + await user.type(labelEl, "foo bar"); + await user.click(screen.getByTestId("submit-button")); + await waitFor(() => { + expect(submitCallback).toHaveBeenCalledWith(expectedFormPostData); + }); + + await user.click(screen.getByTestId("cancel-button")); + expect(cancelCallback).toHaveBeenCalled(); + }); +}); diff --git a/app/assets/js/components/Project/project.gql.js b/app/assets/js/components/Project/project.gql.js index d3950e80d..79d03f394 100644 --- a/app/assets/js/components/Project/project.gql.js +++ b/app/assets/js/components/Project/project.gql.js @@ -60,6 +60,20 @@ export const GET_PROJECTS = gql` } `; +export const PROJECTS_SEARCH = gql` + query ProjectsSearch($query: String!) { + projectsSearch(query: $query) { + id + title + folder + updatedAt + ingestSheets { + id + } + } + } +`; + export const INGEST_SHEET_STATUS_UPDATES_FOR_PROJECT_SUBSCRIPTION = gql` subscription IngestSheetUpdatesForProject($projectId: ID!) { ingestSheetUpdatesForProject(projectId: $projectId) { diff --git a/app/assets/js/components/Project/project.gql.mock.js b/app/assets/js/components/Project/project.gql.mock.js index fd3c6fa5b..e4a8a2b68 100644 --- a/app/assets/js/components/Project/project.gql.mock.js +++ b/app/assets/js/components/Project/project.gql.mock.js @@ -2,6 +2,7 @@ import { GET_PROJECTS, GET_PROJECT, INGEST_SHEET_STATUS_UPDATES_FOR_PROJECT_SUBSCRIPTION, + PROJECTS_SEARCH, } from "./project.gql.js"; export const MOCK_PROJECT_TITLE = "Mock project title"; @@ -101,3 +102,19 @@ export const getProjectsMock = { }, }, }; + +export const projectsSearchMock = (searchTerm) => { + return { + request: { + query: PROJECTS_SEARCH, + variables: { + query: searchTerm, + }, + }, + result: { + data: { + projectsSearch: mockProjects, + }, + }, + } +}; diff --git a/app/assets/js/screens/Project/List.jsx b/app/assets/js/screens/Project/List.jsx index 8c59d737f..8ea2dafcc 100644 --- a/app/assets/js/screens/Project/List.jsx +++ b/app/assets/js/screens/Project/List.jsx @@ -77,11 +77,7 @@ const ScreensProjectList = () => { {!loading && !error && (
- +
)} diff --git a/app/assets/js/screens/Project/List.test.js b/app/assets/js/screens/Project/List.test.js index 028259847..d4914c1e9 100644 --- a/app/assets/js/screens/Project/List.test.js +++ b/app/assets/js/screens/Project/List.test.js @@ -29,8 +29,4 @@ describe("Project List component", () => { expect(await screen.findByTestId("screen-header")); expect(await screen.findByTestId("screen-content")); }); - - it("renders the project list component", async () => { - expect(await screen.findByTestId("project-list")); - }); }); diff --git a/app/lib/meadow/ingest/projects.ex b/app/lib/meadow/ingest/projects.ex index 4b325634a..07c45b835 100644 --- a/app/lib/meadow/ingest/projects.ex +++ b/app/lib/meadow/ingest/projects.ex @@ -83,4 +83,18 @@ defmodule Meadow.Ingest.Projects do def change_project(%Project{} = sheet) do Project.changeset(sheet, %{}) end + + @doc """ + Search projects by title. + + Returns a list of projects matching the given `query`. + """ + def search(query, max_results \\ 100) do + from(p in Project, + where: ilike(p.title, ^"%#{query}%"), + limit: ^max_results, + order_by: [{:desc, :updated_at}] + ) + |> Repo.all() + end end diff --git a/app/lib/meadow_web/resolvers/ingest.ex b/app/lib/meadow_web/resolvers/ingest.ex index c359ac912..42eb111bb 100644 --- a/app/lib/meadow_web/resolvers/ingest.ex +++ b/app/lib/meadow_web/resolvers/ingest.ex @@ -61,6 +61,10 @@ defmodule MeadowWeb.Resolvers.Ingest do end end + def search_projects(_, %{query: query}, _) do + {:ok, Projects.search(query)} + end + def ingest_sheet(_, %{id: id}, _) do {:ok, Sheets.get_ingest_sheet!(id)} end diff --git a/app/lib/meadow_web/schema/types/ingest_types.ex b/app/lib/meadow_web/schema/types/ingest_types.ex index aec92ca61..c8c63984b 100644 --- a/app/lib/meadow_web/schema/types/ingest_types.ex +++ b/app/lib/meadow_web/schema/types/ingest_types.ex @@ -19,6 +19,13 @@ defmodule MeadowWeb.Schema.IngestTypes do resolve(&MeadowWeb.Resolvers.Ingest.projects/3) end + @desc "Search for projects by title" + field :projects_search, list_of(:project) do + arg(:query, non_null(:string)) + middleware(Middleware.Authenticate) + resolve(&MeadowWeb.Resolvers.Ingest.search_projects/3) + end + @desc "Get a project by its id" field :project, :project do arg(:id, non_null(:id)) diff --git a/app/test/meadow/ingest/projects_test.exs b/app/test/meadow/ingest/projects_test.exs index ac399e8b6..dbfc03f85 100644 --- a/app/test/meadow/ingest/projects_test.exs +++ b/app/test/meadow/ingest/projects_test.exs @@ -14,6 +14,12 @@ defmodule Meadow.Ingest.ProjectsTest do assert Projects.list_projects() == [project] end + test "projects_search/0 returns list of matched projects" do + project = project_fixture(@valid_attrs) + assert Projects.search("some title") == [project] + assert Projects.search("nothing") == [] + end + test "get_project!/1 returns the project with given id" do project = project_fixture(@valid_attrs) assert Projects.get_project!(project.id) == project diff --git a/app/test/meadow_web/schema/query/projects_search_test.exs b/app/test/meadow_web/schema/query/projects_search_test.exs new file mode 100644 index 000000000..960d73ada --- /dev/null +++ b/app/test/meadow_web/schema/query/projects_search_test.exs @@ -0,0 +1,59 @@ +defmodule MeadowWeb.Schema.Query.ProjectsTest do + defmodule All do + use Meadow.DataCase + use MeadowWeb.ConnCase, async: true + use Wormwood.GQLCase + + set_gql(MeadowWeb.Schema, """ + query($query: String!) { + projectsSearch(query: $query){ + title + } + } + """) + + test "projects search is a valid query" do + projects_fixture() + + result = + query_gql( + variables: %{"query" => "Project"}, + context: gql_context() + ) + + assert {:ok, query_data} = result + + projects = get_in(query_data, [:data, "projectsSearch"]) + assert length(projects) == 3 + end + end + + defmodule Search do + use Meadow.DataCase + use MeadowWeb.ConnCase, async: true + use Wormwood.GQLCase + + set_gql(MeadowWeb.Schema, """ + query($query: String!) { + projectsSearch(query: $query){ + title + } + } + """) + + test "search project title with query string" do + projects_fixture() + + result = + query_gql( + variables: %{"query" => "2"}, + context: gql_context() + ) + + assert {:ok, query_data} = result + + projects = get_in(query_data, [:data, "projectsSearch"]) + assert length(projects) == 1 + end + end +end From 0bb6b009bc24e7bc7e4c935876686e90031c7def Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 20 Aug 2024 22:24:19 +0000 Subject: [PATCH 7/8] Update Ark ANVL escaping to include :, \r, \n --- app/lib/meadow/ark/serializer.ex | 15 ++++++- app/test/meadow/ark/serializer_test.exs | 54 +++++++++++++++++++------ 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/app/lib/meadow/ark/serializer.ex b/app/lib/meadow/ark/serializer.ex index 909f6b89d..0dc0d059f 100644 --- a/app/lib/meadow/ark/serializer.ex +++ b/app/lib/meadow/ark/serializer.ex @@ -48,6 +48,17 @@ defmodule Meadow.Ark.Serializer do |> Enum.join("\n") end - def serialize({key, value}) when is_atom(key), - do: Map.get(@datacite_map, key) <> ": " <> String.replace(value, "%", "%25") + def serialize({key, value}) when is_atom(key) do + escapable = + case key do + :target -> "%\r\n" + _ -> ":%\r\n" + end + + [ + Map.get(@datacite_map, key), + URI.encode(value, fn c -> not String.contains?(escapable, <>) end) + ] + |> Enum.join(": ") + end end diff --git a/app/test/meadow/ark/serializer_test.exs b/app/test/meadow/ark/serializer_test.exs index beda91905..09aa2014f 100644 --- a/app/test/meadow/ark/serializer_test.exs +++ b/app/test/meadow/ark/serializer_test.exs @@ -3,7 +3,16 @@ defmodule Meadow.Ark.SerializerTest do alias Meadow.Ark.Serializer - @response_body "success: ark:/99999/fk4z90ps4x\n_updated: 1630613597\ndatacite.publisher: Test publisher\n_profile: datacite\ndatacite.title: Test title\n_export: yes\ndatacite.creator: Test creator\n_owner: apitest\n_ownergroup: apitest\n_target: https://test/items/123\n_created: 1630613597\ndatacite.publicationyear: 2021\ndatacite.resourcetype: Image\n_status: public\n" + @request_payload """ + _profile: datacite + datacite.creator: Test %25 creator + datacite.publicationyear: 2021 + datacite.publisher: Publisher%3A Test + datacite.resourcetype: Image + _status: public + _target: https://test/items/123 + datacite.title: 100%25 + """ describe "serialize/1" do test "desconstructs a Meadow.Ark and properly handles ANVL escaping of % characters" do @@ -11,29 +20,48 @@ defmodule Meadow.Ark.SerializerTest do ark: "ark:/99999/fk4z90ps4x", creator: "Test % creator", publication_year: "2021", - publisher: "%Test publisher%", + publisher: "Publisher: Test", resource_type: "Image", status: "public", target: "https://test/items/123", title: "100%" } - assert Serializer.serialize(ark) == "_profile: datacite\ndatacite.creator: Test %25 creator\ndatacite.publicationyear: 2021\ndatacite.publisher: %25Test publisher%25\ndatacite.resourcetype: Image\n_status: public\n_target: https://test/items/123\ndatacite.title: 100%25" + assert Serializer.serialize(ark) == String.trim(@request_payload) end end + @response_body """ + success: ark:/99999/fk4z90ps4x + _updated: 1630613597 + datacite.publisher: Test publisher + _profile: datacite + datacite.title: Test title + _export: yes + datacite.creator: Test creator + _owner: apitest + _ownergroup: apitest + _target: https://test/items/123 + _created: 1630613597 + datacite.publicationyear: 2021 + datacite.resourcetype: Image + _status: public + """ + describe "deserialize/1" do test "builds a Meadow.Ark struct" do - assert %Meadow.Ark{ - ark: "ark:/99999/fk4z90ps4x", - creator: "Test creator", - publication_year: "2021", - publisher: "Test publisher", - resource_type: "Image", - status: "public", - target: "https://test/items/123", - title: "Test title" - } = Serializer.deserialize(@response_body) + expected = %Meadow.Ark{ + ark: "ark:/99999/fk4z90ps4x", + creator: "Test creator", + publication_year: "2021", + publisher: "Test publisher", + resource_type: "Image", + status: "public", + target: "https://test/items/123", + title: "Test title" + } + + assert Serializer.deserialize(@response_body) == expected end end end From 58e33aebc692f6a98eefdbfa04bdbfd7f2b69aff Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 20 Aug 2024 22:24:33 +0000 Subject: [PATCH 8/8] Fix compiler warnings --- app/lib/meadow/search/bulk.ex | 6 +++--- app/lib/meadow/search/config.ex | 2 -- app/lib/meadow/utils/arks.ex | 2 +- app/lib/meadow_web/resolvers/data.ex | 14 -------------- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/app/lib/meadow/search/bulk.ex b/app/lib/meadow/search/bulk.ex index 8c86572fc..d26d73bb3 100644 --- a/app/lib/meadow/search/bulk.ex +++ b/app/lib/meadow/search/bulk.ex @@ -40,16 +40,16 @@ defmodule Meadow.Search.Bulk do defp upload_batch(docs, index) do with_log_metadata module: __MODULE__, index: index do bulk_document = docs |> Enum.join("\n") - + Logger.info("Uploading batch of #{Enum.count(docs)} documents to #{index}") - + case HTTP.post("/#{index}/_bulk", bulk_document <> "\n") do {:ok, %{status_code: status} = response} -> Logger.info("Bulk upload status: #{status}") {:ok, response} {:retry, response} -> - Logger.warn("Bulk upload retrying") + Logger.warning("Bulk upload retrying") {:retry, response} {:error, error} -> diff --git a/app/lib/meadow/search/config.ex b/app/lib/meadow/search/config.ex index 13c3ee1bf..300437505 100644 --- a/app/lib/meadow/search/config.ex +++ b/app/lib/meadow/search/config.ex @@ -2,8 +2,6 @@ defmodule Meadow.Search.Config do @moduledoc """ Convenience methods for retrieving search-specific configuration """ - alias Meadow.Search.HTTP - require Logger def index_configs do diff --git a/app/lib/meadow/utils/arks.ex b/app/lib/meadow/utils/arks.ex index 74cf9677a..f85a61608 100644 --- a/app/lib/meadow/utils/arks.ex +++ b/app/lib/meadow/utils/arks.ex @@ -52,7 +52,7 @@ defmodule Meadow.Arks do """ def mint_ark(%Work{descriptive_metadata: %{ark: ark}} = work) when not is_nil(ark) do - Logger.warn("Not minting ARK for work #{work.id} because it already has one: #{ark}") + Logger.warning("Not minting ARK for work #{work.id} because it already has one: #{ark}") {:noop, work} end diff --git a/app/lib/meadow_web/resolvers/data.ex b/app/lib/meadow_web/resolvers/data.ex index 2978417b1..3f9d83152 100644 --- a/app/lib/meadow_web/resolvers/data.ex +++ b/app/lib/meadow_web/resolvers/data.ex @@ -164,20 +164,6 @@ defmodule MeadowWeb.Resolvers.Data do end end - def replace_file_set(_, %{id: id} = params, _) do - file_set = FileSets.get_file_set!(id) - - case Pipeline.replace_the_file_set(file_set, Map.delete(params, :id)) do - {:error, changeset} -> - {:error, - message: "Could not replace file set", - details: ChangesetErrors.humanize_errors(changeset)} - - {:ok, file_set} -> - {:ok, file_set} - end - end - def update_file_set(_, %{id: id} = params, _) do file_set = FileSets.get_file_set!(id)