diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index de91478a3e..175920ce60 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -6,13 +6,17 @@ }, "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4e3d09f", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#17f3bf5", "axios": "^1.4.0", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", + "graphql-request": "^6.1.0", + "jsonwebtoken": "^9.0.2", + "lodash.zipobject": "^4.1.3", "nock": "^13.3.1" }, "devDependencies": { + "@types/lodash.zipobject": "^4.1.7", "@types/node": "18.16.1", "ts-node": "^10.9.1", "typescript": "^5.1.6" diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index 199b1e8691..b2ba3da222 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#4e3d09f - version: github.com/theopensystemslab/planx-core/4e3d09f + specifier: git+https://github.com/theopensystemslab/planx-core#17f3bf5 + version: github.com/theopensystemslab/planx-core/17f3bf5 axios: specifier: ^1.4.0 version: 1.4.0 @@ -20,11 +20,23 @@ dependencies: dotenv-expand: specifier: ^10.0.0 version: 10.0.0 + graphql-request: + specifier: ^6.1.0 + version: 6.1.0(graphql@16.8.0) + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 + lodash.zipobject: + specifier: ^4.1.3 + version: 4.1.3 nock: specifier: ^13.3.1 version: 13.3.1 devDependencies: + '@types/lodash.zipobject': + specifier: ^4.1.7 + version: 4.1.7 '@types/node': specifier: 18.16.1 version: 18.16.1 @@ -369,13 +381,13 @@ packages: resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} dev: false - /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.48.0 + eslint: 8.49.0 eslint-visitor-keys: 3.4.3 dev: false @@ -401,8 +413,8 @@ packages: - supports-color dev: false - /@eslint/js@8.48.0: - resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} + /@eslint/js@8.49.0: + resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false @@ -442,8 +454,8 @@ packages: graphql: 16.8.0 dev: false - /@humanwhocodes/config-array@0.11.10: - resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 @@ -482,8 +494,8 @@ packages: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} dev: false - /@mui/base@5.0.0-beta.13(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-uC0l97pBspfDAp+iz2cJq8YZ8Sd9i73V77+WzUiOAckIVEyCm5dyVDZCCO2/phmzckVEeZCGcytybkjMQuhPQw==} + /@mui/base@5.0.0-beta.14(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Je/9JzzYObsuLCIClgE8XvXNFb55IEz8n2NtStUfASfNiVrwiR8t6VVFFuhofehkyTIN34tq1qbBaOjCnOovBw==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -497,7 +509,7 @@ packages: '@emotion/is-prop-valid': 1.2.1 '@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0) '@mui/types': 7.2.4 - '@mui/utils': 5.14.7(react@18.2.0) + '@mui/utils': 5.14.8(react@18.2.0) '@popperjs/core': 2.11.8 clsx: 2.0.0 prop-types: 15.8.1 @@ -506,12 +518,12 @@ packages: react-is: 18.2.0 dev: false - /@mui/core-downloads-tracker@5.14.7: - resolution: {integrity: sha512-sCWTUNElBPgB30iLvWe3PU7SIlTKZNf6/E/sko85iHVeHCM6WPkDw+y89CrZYjhFNmPqt2fIQM/pZu+rP2lFLA==} + /@mui/core-downloads-tracker@5.14.8: + resolution: {integrity: sha512-8V7ZOC/lKkM03TRHqaThQFIq6bWPnj7L/ZWPh0ymldYFFyh8XdF0ywTgafsofDNYT4StlNknbaTjVHBma3SNjQ==} dev: false - /@mui/material@5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-jIZj9F7zMv6IlyaYDVv5M2Kp20jIX8c0kzuwteySHS/A0IvPVyomQEPtWc51MCbpDNCqzwoZUp3rQtA2lI8k7A==} + /@mui/material@5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-fqvDGGF1pXwOOL/f0Gw+KHo/67hasRpf2ApTIJkbuONOk9AUb2jnYMEqCWmL2sUcbbE3ShMbHl8N7HPSsRv1/A==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -530,11 +542,11 @@ packages: '@babel/runtime': 7.22.11 '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) - '@mui/base': 5.0.0-beta.13(react-dom@18.2.0)(react@18.2.0) - '@mui/core-downloads-tracker': 5.14.7 - '@mui/system': 5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) + '@mui/base': 5.0.0-beta.14(react-dom@18.2.0)(react@18.2.0) + '@mui/core-downloads-tracker': 5.14.8 + '@mui/system': 5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) '@mui/types': 7.2.4 - '@mui/utils': 5.14.7(react@18.2.0) + '@mui/utils': 5.14.8(react@18.2.0) '@types/react-transition-group': 4.4.6 clsx: 2.0.0 csstype: 3.1.2 @@ -545,8 +557,8 @@ packages: react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false - /@mui/private-theming@5.14.7(react@18.2.0): - resolution: {integrity: sha512-Y86+hmDnJab2Ka42PgxKpK3oL7EiacbeeX3X/lG9LGO0wSc45wZjHeTfIlVSkkUCkexiMKEJp5NlSjZhr27NRQ==} + /@mui/private-theming@5.14.8(react@18.2.0): + resolution: {integrity: sha512-iBzpcl3Mh92XaYpYPdgzzRxNGkjpoDz8rf8/q5m+EBPowFEHV+CCS9hC0Q2pOKLW3VFFikA7w/GHt7n++40JGQ==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -556,13 +568,13 @@ packages: optional: true dependencies: '@babel/runtime': 7.22.11 - '@mui/utils': 5.14.7(react@18.2.0) + '@mui/utils': 5.14.8(react@18.2.0) prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/styled-engine@5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-hKBETEDsIAkL8/mBwPiQj/vw28OeIhMXC3Tvj4J2bb9snxAKpiZioR1PwqP+6P41twsC/GKBd0Vr9oaWYaHuMg==} + /@mui/styled-engine@5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): + resolution: {integrity: sha512-LGwOav/Y40PZWZ2yDk4beUoRlc57Vg+Vpxi9V9BBtT2ESAucCgFobkt+T8eVLMWF9huUou5pwKgLSU5pF90hBg==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.4.1 @@ -583,8 +595,8 @@ packages: react: 18.2.0 dev: false - /@mui/system@5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-jeZtHglc+Pi6qjGoopT6O4RqYXVBMqHVOsjMGP0hxGSSPm1T4gsAu7jU8eqGx9YwwjvvJ0eotTjFqw7iJ6qE2Q==} + /@mui/system@5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): + resolution: {integrity: sha512-Dxnasv7Pj5hYe4ZZFKJZu4ufKm6cxpitWt3A+qMPps22YhqyeEqgDBq/HsAB3GOjqDP40fTAvQvS/Hguf4SJuw==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -602,10 +614,10 @@ packages: '@babel/runtime': 7.22.11 '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) - '@mui/private-theming': 5.14.7(react@18.2.0) - '@mui/styled-engine': 5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) + '@mui/private-theming': 5.14.8(react@18.2.0) + '@mui/styled-engine': 5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) '@mui/types': 7.2.4 - '@mui/utils': 5.14.7(react@18.2.0) + '@mui/utils': 5.14.8(react@18.2.0) clsx: 2.0.0 csstype: 3.1.2 prop-types: 15.8.1 @@ -621,8 +633,8 @@ packages: optional: true dev: false - /@mui/utils@5.14.7(react@18.2.0): - resolution: {integrity: sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw==} + /@mui/utils@5.14.8(react@18.2.0): + resolution: {integrity: sha512-1Ls2FfyY2yVSz9NEqedh3J8JAbbZAnUWkOWLE2f4/Hc4T5UWHMfzBLLrCqExfqyfyU+uXYJPGeNIsky6f8Gh5Q==} engines: {node: '>=12.0.0'} peerDependencies: react: ^17.0.0 || ^18.0.0 @@ -696,9 +708,14 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: false + /@types/lodash.zipobject@4.1.7: + resolution: {integrity: sha512-bsFXX/ac3fFgW3l/yxwRx7NvTXryi4bMaNcsbSK2MJnTPn0nHvs7NdwfHtvOkNKxSQ0dXgnNwI5oEGLoMA1mug==} + dependencies: + '@types/lodash': 4.14.196 + dev: true + /@types/lodash@4.14.196: resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} - dev: false /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} @@ -885,6 +902,10 @@ packages: concat-map: 0.0.1 dev: false + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false @@ -1115,6 +1136,12 @@ packages: engines: {node: '>=12'} dev: false + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.1.2 + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false @@ -1188,16 +1215,16 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false - /eslint@8.48.0: - resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} + /eslint@8.49.0: + resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.48.0 - '@humanwhocodes/config-array': 0.11.10 + '@eslint/js': 8.49.0 + '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 @@ -1588,8 +1615,8 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: false - /json-schema-to-typescript@13.0.2: - resolution: {integrity: sha512-TCaEVW4aI2FmMQe7f98mvr3/oiVmXEC1xZjkTZ9L/BSoTXFlC7p64mD5AD2d8XWycNBQZUnHwXL5iVXt1HWwNQ==} + /json-schema-to-typescript@13.1.1: + resolution: {integrity: sha512-F3CYhtA7F3yPbb8vF7sFchk/2dnr1/yTKf8RcvoNpjnh67ZS/ZMH1ElLt5KHAtf2/bymiejLQQszszPWEeTdSw==} engines: {node: '>=12.0.0'} hasBin: true dependencies: @@ -1625,6 +1652,22 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: false + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.2 + semver: 7.5.4 + dev: false + /jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} dependencies: @@ -1634,6 +1677,21 @@ packages: setimmediate: 1.0.5 dev: false + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.1.2 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.1.2 + dev: false + /knuth-shuffle-seeded@1.0.6: resolution: {integrity: sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==} dependencies: @@ -1685,14 +1743,38 @@ packages: resolution: {integrity: sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==} dev: false + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + /lodash.isempty@4.4.0: resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} dev: false + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + /lodash.isnil@4.0.0: resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} dev: false + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} dev: false @@ -1709,6 +1791,10 @@ packages: resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==} dev: false + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash.property@4.4.2: resolution: {integrity: sha512-WVnsHSCea5NFrsWGdZilCFJWhHFtb/nqVrDXzR1bIXmOxIykrsAKOpRaYu9rCYoHYuq1SnG6G4A0inMwShV4dg==} dev: false @@ -1721,6 +1807,10 @@ packages: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} dev: false + /lodash.zipobject@4.1.3: + resolution: {integrity: sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==} + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: false @@ -1968,8 +2058,8 @@ packages: hasBin: true dev: false - /prettier@3.0.2: - resolution: {integrity: sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==} + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} engines: {node: '>=14'} hasBin: true dev: false @@ -2167,6 +2257,14 @@ packages: lru-cache: 6.0.0 dev: false + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: false @@ -2498,8 +2596,8 @@ packages: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: false - github.com/theopensystemslab/planx-core/4e3d09f: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4e3d09f} + github.com/theopensystemslab/planx-core/17f3bf5: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/17f3bf5} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2507,16 +2605,16 @@ packages: dependencies: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) - '@mui/material': 5.14.7(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) + '@mui/material': 5.14.8(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) '@types/geojson': 7946.0.10 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) docx: 8.2.2 - eslint: 8.48.0 + eslint: 8.49.0 fast-xml-parser: 4.2.7 graphql: 16.8.0 graphql-request: 6.1.0(graphql@16.8.0) - json-schema-to-typescript: 13.0.2 + json-schema-to-typescript: 13.1.1 lodash.capitalize: 4.2.1 lodash.get: 4.4.2 lodash.groupby: 4.6.0 @@ -2528,7 +2626,7 @@ packages: lodash.property: 4.4.2 lodash.set: 4.3.2 lodash.startcase: 4.4.0 - prettier: 3.0.2 + prettier: 3.0.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) type-fest: 4.3.1 diff --git a/e2e/tests/api-driven/src/client.ts b/e2e/tests/api-driven/src/client.ts index 808dbfb62c..78ac350f86 100644 --- a/e2e/tests/api-driven/src/client.ts +++ b/e2e/tests/api-driven/src/client.ts @@ -1,5 +1,7 @@ import assert from "node:assert"; import { CoreDomainClient } from "@opensystemslab/planx-core"; +import { TEST_EMAIL } from "../../ui-driven/src/helpers"; +import { buildJWT } from "./jwt"; // check env variables are defined assert(process.env.HASURA_GRAPHQL_ADMIN_SECRET); @@ -11,6 +13,21 @@ const targetURL = process.env.HASURA_GRAPHQL_URL!.replace( ); export const $admin = new CoreDomainClient({ - hasuraSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET!, + auth: { adminSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET! }, targetURL, }); + +/** + * Get client authorised to the permissions level of the test user + */ +export const getClient = async () => { + const jwt = await buildJWT(TEST_EMAIL); + if (!jwt) throw Error("Unable to generate JWT for test user"); + + const client = new CoreDomainClient({ + targetURL: process.env.HASURA_GRAPHQL_URL!, + auth: { jwt }, + }); + + return client; +}; diff --git a/e2e/tests/api-driven/src/helpers.ts b/e2e/tests/api-driven/src/helpers.ts new file mode 100644 index 0000000000..9d73abd164 --- /dev/null +++ b/e2e/tests/api-driven/src/helpers.ts @@ -0,0 +1,61 @@ +import { TEST_EMAIL } from "../../ui-driven/src/helpers"; +import { $admin } from "./client"; + +export async function createTeam() { + return $admin.team.create({ + name: "E2E Test Team", + slug: "E2E", + logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", + primaryColor: "#444444", + submissionEmail: TEST_EMAIL, + homepage: "planx.uk", + }); +} + +export async function createUser() { + return $admin.user.create({ + firstName: "Test", + lastName: "Test", + email: TEST_EMAIL, + }); +} + +export async function tearDownTestContext({ + teamId, + userId, + flowId, + publishedFlowId, + sessionId, + paymentRequestId, +}: { + teamId?: number; + userId?: number; + flowId?: string; + publishedFlowId?: number; + sessionId?: string; + paymentRequestId?: string; +}) { + if (paymentRequestId) { + await $admin.paymentRequest._destroy(paymentRequestId); + } + if (sessionId) { + await $admin.application._destroyAll(sessionId); + await $admin.session._destroy(sessionId); + } + if (publishedFlowId) { + await $admin.flow._destroyPublished(publishedFlowId); + } + if (flowId) { + await $admin.flow._destroy(flowId); + } + if (userId) { + await $admin.user._destroy(userId); + } + if (teamId) { + await $admin.team._destroy(teamId); + } +} + +export async function getTestUser() { + return await $admin.user.getByEmail(TEST_EMAIL); +} diff --git a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts index b7175fa247..777e44c3ba 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts @@ -25,25 +25,6 @@ export async function setUpMocks() { }); } -export async function createTeam() { - return $admin.team.create({ - name: "E2E Test Team", - slug: "E2E", - logo: "https://raw.githubusercontent.com/theopensystemslab/planx-team-logos/main/planx-testing.svg", - primaryColor: "#444444", - submissionEmail: TEST_EMAIL, - homepage: "planx.uk", - }); -} - -export async function createUser() { - return $admin.user.create({ - firstName: "Test", - lastName: "Test", - email: TEST_EMAIL, - }); -} - export async function buildITPFlow({ userId, teamId, @@ -146,39 +127,3 @@ export async function getSessionSubmittedAt( const detailedSession = await $admin.session.findDetails(sessionId); return detailedSession?.submittedAt; } - -export async function tearDownTestContext({ - teamId, - userId, - flowId, - publishedFlowId, - sessionId, - paymentRequestId, -}: { - teamId?: number; - userId?: number; - flowId?: string; - publishedFlowId?: number; - sessionId?: string; - paymentRequestId?: string; -}) { - if (paymentRequestId) { - await $admin.paymentRequest._destroy(paymentRequestId); - } - if (sessionId) { - await $admin.application._destroyAll(sessionId); - await $admin.session._destroy(sessionId); - } - if (publishedFlowId) { - await $admin.flow._destroyPublished(publishedFlowId); - } - if (flowId) { - await $admin.flow._destroy(flowId); - } - if (userId) { - await $admin.user._destroy(userId); - } - if (teamId) { - await $admin.team._destroy(teamId); - } -} diff --git a/e2e/tests/api-driven/src/invite-to-pay/steps.ts b/e2e/tests/api-driven/src/invite-to-pay/steps.ts index 02e6612e3e..42074c4435 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/steps.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/steps.ts @@ -1,16 +1,7 @@ import { strict as assert } from "node:assert"; -import { - Given, - When, - Then, - BeforeAll, - After, - AfterAll, -} from "@cucumber/cucumber"; +import { Given, When, Then, Before, After } from "@cucumber/cucumber"; import { setUpMocks, - createTeam, - createUser, buildITPFlow, buildSessionForFlow, buildPaymentRequestForSession, @@ -18,8 +9,8 @@ import { getSendResponse, getSessionSubmittedAt, waitForResponse, - tearDownTestContext, } from "./helpers"; +import { createTeam, createUser, tearDownTestContext } from "../helpers"; const context: { teamId?: number; @@ -30,7 +21,7 @@ const context: { paymentRequestId?: string; } = {}; -BeforeAll(async () => { +Before("@invite-to-pay", async () => { await setUpMocks(); context.teamId = await createTeam(); if (!context.teamId) { @@ -43,17 +34,7 @@ BeforeAll(async () => { }); // tear down each example but not user and team -After(async () => { - await tearDownTestContext({ - flowId: context.flowId, - publishedFlowId: context.publishedFlowId, - sessionId: context.sessionId, - paymentRequestId: context.paymentRequestId, - }); -}); - -// tear down everything -AfterAll(async () => { +After("@invite-to-pay", async () => { await tearDownTestContext(context); }); diff --git a/e2e/tests/api-driven/src/invite-to-pay/submissions.feature b/e2e/tests/api-driven/src/invite-to-pay/submissions.feature index 1c63c22f79..75e714e34d 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/submissions.feature +++ b/e2e/tests/api-driven/src/invite-to-pay/submissions.feature @@ -1,6 +1,6 @@ Feature: Invite to Pay Submissions - @regression + @regression @invite-to-pay Scenario: Modified `paid_at` on sessions with a valid payment request Given a session with a payment request for an invite to pay flow where "" is a send destination When the payment request's `paid_at` date is set diff --git a/e2e/tests/api-driven/src/jwt.ts b/e2e/tests/api-driven/src/jwt.ts new file mode 100644 index 0000000000..27abe0e0f2 --- /dev/null +++ b/e2e/tests/api-driven/src/jwt.ts @@ -0,0 +1,51 @@ +import { sign } from "jsonwebtoken"; +import { User, Role } from "@opensystemslab/planx-core/types"; +import { $admin } from "./client"; + +// This code is copied from api.planx.uk/modules/auth/service.ts + +export const buildJWT = async (email: string): Promise => { + const user = await $admin.user.getByEmail(email); + if (!user) return; + + const data = { + sub: user.id.toString(), + "https://hasura.io/jwt/claims": generateHasuraClaimsForUser(user), + }; + + const jwt = sign(data, process.env.JWT_SECRET!); + return jwt; +}; + +const generateHasuraClaimsForUser = (user: User) => ({ + "x-hasura-allowed-roles": getAllowedRolesForUser(user), + "x-hasura-default-role": getDefaultRoleForUser(user), + "x-hasura-user-id": user.id.toString(), +}); + +/** + * Get all possible roles for this user + * Requests made outside this scope will not be authorised by Hasura + */ +const getAllowedRolesForUser = (user: User): Role[] => { + const teamRoles = user.teams.map((teamRole) => teamRole.role); + const allowedRoles: Role[] = [ + "public", // Allow public access + "teamEditor", // Least privileged role for authenticated users - required for Editor access + ...teamRoles, // User specific roles + ]; + if (user.isPlatformAdmin) allowedRoles.push("platformAdmin"); + + return [...new Set(allowedRoles)]; +}; + +/** + * The default role is used for all requests + * Can be overwritten on a per-request basis in the client using the x-hasura-role header + * set to a role in the x-hasura-allowed-roles list + * + * This is the role of least privilege for the user + */ +const getDefaultRoleForUser = (user: User): Role => { + return user.isPlatformAdmin ? "platformAdmin" : "teamEditor"; +}; diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index eead6768e2..02257d0ce3 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4e3d09f", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#17f3bf5", "axios": "^1.4.0", "dotenv": "^16.3.1", "eslint": "^8.44.0", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 7a9edbc70f..e50d86c6c6 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#4e3d09f - version: github.com/theopensystemslab/planx-core/4e3d09f + specifier: git+https://github.com/theopensystemslab/planx-core#17f3bf5 + version: github.com/theopensystemslab/planx-core/17f3bf5 axios: specifier: ^1.4.0 version: 1.4.0 @@ -2367,8 +2367,8 @@ packages: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} dev: false - github.com/theopensystemslab/planx-core/4e3d09f: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4e3d09f} + github.com/theopensystemslab/planx-core/17f3bf5: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/17f3bf5} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/e2e/tests/ui-driven/src/context.ts b/e2e/tests/ui-driven/src/context.ts index 3e547e86d0..d188eb4e51 100644 --- a/e2e/tests/ui-driven/src/context.ts +++ b/e2e/tests/ui-driven/src/context.ts @@ -127,7 +127,7 @@ export function getCoreDomainClient(): CoreDomainClient { ); const SECRET = process.env.HASURA_GRAPHQL_ADMIN_SECRET!; return new CoreDomainClient({ - hasuraSecret: SECRET, + auth: { adminSecret: SECRET }, targetURL: API, }); } diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index ffec422e0c..a9545a1e6f 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -14,7 +14,7 @@ "@mui/styles": "^5.14.5", "@mui/utils": "^5.14.5", "@opensystemslab/map": "^0.7.5", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4e3d09f", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#17f3bf5", "@tiptap/core": "^2.0.3", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.6", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index 4625024832..fa02d9204a 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -46,8 +46,8 @@ dependencies: specifier: ^0.7.5 version: 0.7.5 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#4e3d09f - version: github.com/theopensystemslab/planx-core/4e3d09f(@types/react@18.2.20) + specifier: git+https://github.com/theopensystemslab/planx-core#17f3bf5 + version: github.com/theopensystemslab/planx-core/17f3bf5(@types/react@18.2.20) '@tiptap/core': specifier: ^2.0.3 version: 2.0.3(@tiptap/pm@2.0.3) @@ -20690,9 +20690,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/4e3d09f(@types/react@18.2.20): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4e3d09f} - id: github.com/theopensystemslab/planx-core/4e3d09f + github.com/theopensystemslab/planx-core/17f3bf5(@types/react@18.2.20): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/17f3bf5} + id: github.com/theopensystemslab/planx-core/17f3bf5 name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true