From 01ccd9f46b96be7f9c9e169308069630a1346237 Mon Sep 17 00:00:00 2001 From: Jordan Tucker Date: Wed, 4 Nov 2020 10:44:03 +0000 Subject: [PATCH] feat: add gql client (#57) * feat: add gql client Closes: #55 Signed-off-by: Jordan * fix: add placeholder config Signed-off-by: Jordan * feat: add split for config and api separation Also adds hardcoded endpoints on client Signed-off-by: Jordan * refactor: cleanup and review comments Signed-off-by: Jordan * fix: ignore coverage Signed-off-by: Jordan * fix: enable tests on CI Signed-off-by: Jordan * fix: update package-lock Signed-off-by: Jordan --- .github/workflows/node-pr-jobs-secure.yml | 4 +- .../Bootstrap/GraphQLClient/GraphQLClient.ts | 33 +++ client/Bootstrap/GraphQLClient/index.ts | 8 + client/Queries/README.md | 106 +++++++++ package-lock.json | 203 +++++++++++++++++- package.json | 5 + 6 files changed, 349 insertions(+), 10 deletions(-) create mode 100644 client/Bootstrap/GraphQLClient/GraphQLClient.ts create mode 100644 client/Bootstrap/GraphQLClient/index.ts create mode 100644 client/Queries/README.md diff --git a/.github/workflows/node-pr-jobs-secure.yml b/.github/workflows/node-pr-jobs-secure.yml index ef6327da..fea9feca 100644 --- a/.github/workflows/node-pr-jobs-secure.yml +++ b/.github/workflows/node-pr-jobs-secure.yml @@ -48,8 +48,8 @@ jobs: # with: # CLIENT_MASTER_REPORT: ${{ env.CLIENT_MASTER_REPORT }} # SERVER_MASTER_REPORT: ${{ env.SERVER_MASTER_REPORT }} - # - name: Test - # run: npm run test:jest -- --forceExit # force exit in case a test does not clean up after failure + - name: Test + run: npm run test:jest -- --forceExit # force exit in case a test does not clean up after failure # - name: Coverage report # id: coverage # if: ${{ always() }} diff --git a/client/Bootstrap/GraphQLClient/GraphQLClient.ts b/client/Bootstrap/GraphQLClient/GraphQLClient.ts new file mode 100644 index 00000000..cd5fe6de --- /dev/null +++ b/client/Bootstrap/GraphQLClient/GraphQLClient.ts @@ -0,0 +1,33 @@ +/* + * Copyright Strimzi authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +/* istanbul ignore file */ + +import { ApolloClient, HttpLink, split, InMemoryCache } from '@apollo/client'; +import { WebSocketLink } from '@apollo/client/link/ws'; +import { SubscriptionClient } from 'subscriptions-transport-ws'; + +const SERVER_API = 'ws://localhost:3000/api'; + +const subscriptionClient = new SubscriptionClient(SERVER_API, { + reconnect: true, +}); + +const subscriptionLink = new WebSocketLink(subscriptionClient); + +const configLink = new HttpLink({ uri: '/config', fetch }); + +const splitLink = split( + (operation) => operation.getContext().purpose === 'config', + configLink, + subscriptionLink +); + +const apolloClient = new ApolloClient({ + link: splitLink, + cache: new InMemoryCache(), +}); + +export { apolloClient }; diff --git a/client/Bootstrap/GraphQLClient/index.ts b/client/Bootstrap/GraphQLClient/index.ts new file mode 100644 index 00000000..601452fd --- /dev/null +++ b/client/Bootstrap/GraphQLClient/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Strimzi authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +/* istanbul ignore file */ + +export { apolloClient } from './GraphQLClient'; diff --git a/client/Queries/README.md b/client/Queries/README.md new file mode 100644 index 00000000..6697987d --- /dev/null +++ b/client/Queries/README.md @@ -0,0 +1,106 @@ +# Queries + +This directory is home to all GraphQL queries. These should be imported by the appropriate models and exposed via a hook. + +## File Structure + +- Queries + - query set (e.g. topics) + - index.ts - containing all gql queries for that set + +## Examples + +### API + +```typescript +// topics/index.ts + +import gql from 'graphql-tag'; + +export const GET_TOPICS = gql` + query { + ... + } +`; + +export const TOPIC_SUBSCRIPTION = gql` + subscription { + ... + } +`; + +export const CREATE_TOPIC = gql` + mutation { + ... + } +`; +``` + +```typescript +// useTopic.hook.ts + +import { CREATE_TOPIC, TOPIC_SUBSCRIPTION, GET_TOPICS } from '@/Queries/topics'; +import { useMutation, useQuery, useSubscription } from '@apollo/client'; + +const useTopic = () => { + const [createTopic, { data }] = useMutation(CREATE_TOPIC); + const getTopics = () => useQuery(GET_TOPICS); + const topicsSubscription = () => + useSubscription(TOPIC_SUBSCRIPTION, {}, true); + return { + addTopic, + getTopics, + topicsSubscription, + }; +}; + +export default useTopic; +``` + +```typescript +// topics.model.ts + +import { useTopic } from '@/Hooks'; + +const TopicModel = () => { + const { addTopic, getTopics, topicSubscription } = useTopic(); + + // pass these as props to view + const { loading, error, data } = getTopics(); + const { sub_loading, sub_error, sub_data } = topicsSubscription(); + ... +}; +``` + +### Config + +When fetching config, a context will need to be provided so that Apollo goes to the correct server. + +```typescript +// config/index.ts + +import gql from 'graphql-tag'; + +const GET_CONFIG = gql` + query { + ... + } +`; +``` + +```typescript +// config.hook.ts + +import { GET_CONFIG } from '@/Queries/topics'; +import { useQuery } from '@apollo/client'; + +const useConfig = () => { + const getConfig = () => + useQuery(GET_CONFIG, { + context: { + purpose: 'config', + }, + }); + return getConfig; +}; +``` diff --git a/package-lock.json b/package-lock.json index a718d7f9..b957b2b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,41 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@apollo/client": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.2.5.tgz", + "integrity": "sha512-zpruxnFMz6K94gs2pqc3sidzFDbQpKT5D6P/J/I9s8ekHZ5eczgnRp6pqXC86Bh7+44j/btpmOT0kwiboyqTnA==", + "requires": { + "@graphql-typed-document-node/core": "^3.0.0", + "@types/zen-observable": "^0.8.0", + "@wry/context": "^0.5.2", + "@wry/equality": "^0.2.0", + "fast-json-stable-stringify": "^2.0.0", + "graphql-tag": "^2.11.0", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.13.0", + "prop-types": "^15.7.2", + "symbol-observable": "^2.0.0", + "ts-invariant": "^0.4.4", + "tslib": "^1.10.0", + "zen-observable": "^0.8.14" + }, + "dependencies": { + "symbol-observable": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", + "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==" + } + } + }, + "@apollo/react-hooks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-4.0.0.tgz", + "integrity": "sha512-fCu0cbne3gbUl0QbA8X4L33iuuFVQbC5Jo2MIKRK8CyawR6PoxDpFdFA1kc6033ODZuZZ9Eo4RdeJFlFIIYcLA==", + "requires": { + "@apollo/client": "^3.2.5" + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -2195,6 +2230,11 @@ } } }, + "@graphql-typed-document-node/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz", + "integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg==" + }, "@icons/material": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", @@ -4957,9 +4997,9 @@ } }, "@types/react": { - "version": "16.9.53", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz", - "integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==", + "version": "16.9.54", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.54.tgz", + "integrity": "sha512-GhawhYraQZpGFO2hVMArjPrYbnA/6+DS8SubK8IPhhVClmKqANihsRenOm5E0mvqK0m/BKoqVktA1O1+Xvlz9w==", "dev": true, "requires": { "@types/prop-types": "*", @@ -5139,6 +5179,11 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@types/zen-observable": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.1.tgz", + "integrity": "sha512-wmk0xQI6Yy7Fs/il4EpOcflG4uonUpYGqvZARESLc2oy4u69fkatFLbJOeW4Q6awO15P4rduAe6xkwHevpXcUQ==" + }, "@typescript-eslint/eslint-plugin": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.6.0.tgz", @@ -5521,6 +5566,22 @@ } } }, + "@wry/context": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz", + "integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@wry/equality": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.2.0.tgz", + "integrity": "sha512-Y4d+WH6hs+KZJUC8YKLYGarjGekBrhslDbf/R20oV+AakHPINSitHfDRQz3EGcEWc1luXYNUvMhawWtZVWNGvQ==", + "requires": { + "tslib": "^1.9.3" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -5811,6 +5872,58 @@ "picomatch": "^2.0.4" } }, + "apollo-link": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz", + "integrity": "sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.21" + } + }, + "apollo-link-http": { + "version": "1.5.17", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz", + "integrity": "sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg==", + "requires": { + "apollo-link": "^1.2.14", + "apollo-link-http-common": "^0.2.16", + "tslib": "^1.9.3" + } + }, + "apollo-link-http-common": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz", + "integrity": "sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==", + "requires": { + "apollo-link": "^1.2.14", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "apollo-utilities": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz", + "integrity": "sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "@wry/equality": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz", + "integrity": "sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==", + "requires": { + "tslib": "^1.9.3" + } + } + } + }, "app-root-dir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", @@ -6190,8 +6303,7 @@ "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", @@ -7058,6 +7170,11 @@ "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", "dev": true }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", @@ -12733,6 +12850,16 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, + "graphql": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.4.0.tgz", + "integrity": "sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA==" + }, + "graphql-tag": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.11.0.tgz", + "integrity": "sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA==" + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -13008,7 +13135,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, "requires": { "react-is": "^16.7.0" } @@ -14445,6 +14571,11 @@ "istanbul-lib-report": "^3.0.0" } }, + "iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, "iterate-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", @@ -21104,6 +21235,14 @@ "is-wsl": "^1.1.0" } }, + "optimism": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.13.0.tgz", + "integrity": "sha512-6JAh3dH+YUE4QUdsgUw8nUQyrNeBKfAEKOHMlLkQ168KhIYFIxzPsHakWrRXDnTO+x61RJrS3/2uEt6W0xlocA==", + "requires": { + "@wry/context": "^0.5.2" + } + }, "optimize-css-assets-webpack-plugin": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz", @@ -26490,6 +26629,33 @@ "minimist": "^1.1.0" } }, + "subscriptions-transport-ws": { + "version": "0.9.18", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.18.tgz", + "integrity": "sha512-tztzcBTNoEbuErsVQpTN2xUNN/efAZXyCyL5m3x4t6SKrEiTL2N8SaKWBFWM4u56pL79ULif3zjyeq+oV+nOaA==", + "requires": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "sugarss": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", @@ -26645,8 +26811,7 @@ "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.4", @@ -27273,6 +27438,14 @@ "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==", "dev": true }, + "ts-invariant": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz", + "integrity": "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==", + "requires": { + "tslib": "^1.9.3" + } + }, "ts-jest": { "version": "26.4.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.3.tgz", @@ -29913,6 +30086,20 @@ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "zen-observable-ts": { + "version": "0.8.21", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz", + "integrity": "sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index ba0aa64e..fe572fc8 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,13 @@ "bugs": {}, "homepage": "https://github.com/strimzi/strimzi-ui#readme", "dependencies": { + "@apollo/client": "^3.2.5", + "@apollo/react-hooks": "^4.0.0", + "apollo-link-http": "^1.5.17", "compression-webpack-plugin": "^4.0.0", "express": "^4.17.1", "express-static-gzip": "^2.0.8", + "graphql": "^15.4.0", "helmet": "^4.1.1", "html-webpack-plugin": "^4.3.0", "http-proxy": "^1.18.1", @@ -53,6 +57,7 @@ "optimize-css-assets-webpack-plugin": "^5.0.3", "react": "^16.13.1", "sass": "^1.26.10", + "subscriptions-transport-ws": "^0.9.18", "terser-webpack-plugin": "^3.0.7", "typescript": "^4.0.3", "webpack": "^4.43.0"