@graphcommerce/graphql 9.0.0-canary.114 → 9.0.0-canary.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/{test → __playwright__}/apolloClient.fixture.ts +3 -4
- package/components/{InContextMask/InContextMask.tsx → PrivateQueryMask/PrivateQueryMask.tsx} +21 -21
- package/config.ts +8 -1
- package/hooks/{useInContextQuery.ts → usePrivateQuery.ts} +23 -20
- package/hooks/{useInContextInput.ts → usePrivateQueryContext.ts} +5 -5
- package/index.ts +3 -3
- package/link/RemovePrivateContextDirectivesLink.ts +19 -0
- package/package.json +9 -8
- package/schema/InContext.graphqls +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 9.0.0-canary.116
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#2452](https://github.com/graphcommerce-org/graphcommerce/pull/2452) [`5dfd3b2`](https://github.com/graphcommerce-org/graphcommerce/commit/5dfd3b201255ef35263485d04153d37bb7e4fe67) - Renamed useInContextQuery to usePrivateQuery ([@paales](https://github.com/paales))
|
|
8
|
+
|
|
9
|
+
## 9.0.0-canary.115
|
|
10
|
+
|
|
3
11
|
## 9.0.0-canary.114
|
|
4
12
|
|
|
5
13
|
## 9.0.0-canary.113
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { Page } from '@playwright/test'
|
|
5
5
|
import { test as base } from '@playwright/test'
|
|
6
6
|
import type { FetchResult, NormalizedCacheObject, TypedDocumentNode } from '../apollo'
|
|
7
|
-
import { ApolloClient,
|
|
7
|
+
import { ApolloClient, getOperationName, InMemoryCache } from '../apollo'
|
|
8
8
|
|
|
9
9
|
type ApolloClientTest = {
|
|
10
10
|
apolloClient: ApolloClient<NormalizedCacheObject>
|
|
@@ -27,7 +27,8 @@ export async function waitForGraphQlResponse<Q, V>(
|
|
|
27
27
|
return (await response?.json()) as FetchResult<Q>
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
/** @public */
|
|
31
|
+
export const test = base.extend<ApolloClientTest>({
|
|
31
32
|
apolloClient: async ({}, use) => {
|
|
32
33
|
const client = new ApolloClient({
|
|
33
34
|
uri: 'http://localhost:3000/api/graphql',
|
|
@@ -37,5 +38,3 @@ const test = base.extend<ApolloClientTest>({
|
|
|
37
38
|
await use(client)
|
|
38
39
|
},
|
|
39
40
|
})
|
|
40
|
-
|
|
41
|
-
export { test }
|
package/components/{InContextMask/InContextMask.tsx → PrivateQueryMask/PrivateQueryMask.tsx}
RENAMED
|
@@ -7,7 +7,7 @@ import React, { createContext, useContext, useMemo } from 'react'
|
|
|
7
7
|
|
|
8
8
|
type MaskProp = { skeleton?: SkeletonProps }
|
|
9
9
|
|
|
10
|
-
interface
|
|
10
|
+
interface PrivateQueryMaskTypeMap<
|
|
11
11
|
AdditionalProps = MaskProp,
|
|
12
12
|
RootComponent extends React.ElementType = 'div',
|
|
13
13
|
> {
|
|
@@ -15,19 +15,21 @@ interface InContextMaskTypeMap<
|
|
|
15
15
|
defaultComponent: RootComponent
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export type
|
|
19
|
-
RootComponent extends React.ElementType =
|
|
18
|
+
export type PrivateQueryMaskProps<
|
|
19
|
+
RootComponent extends React.ElementType = PrivateQueryMaskTypeMap['defaultComponent'],
|
|
20
20
|
AdditionalProps = MaskProp,
|
|
21
|
-
> = OverrideProps<
|
|
21
|
+
> = OverrideProps<PrivateQueryMaskTypeMap<AdditionalProps, RootComponent>, RootComponent> & {
|
|
22
22
|
component?: React.ElementType
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
type
|
|
25
|
+
type PrivateQueryMaskContextType = { mask: boolean }
|
|
26
26
|
|
|
27
|
-
export const
|
|
27
|
+
export const PrivateQueryMaskContext = createContext<PrivateQueryMaskContextType | undefined>(
|
|
28
|
+
undefined,
|
|
29
|
+
)
|
|
28
30
|
|
|
29
|
-
export function
|
|
30
|
-
const context = useContext(
|
|
31
|
+
export function usePrivateQueryMask(): PrivateQueryMaskContextType {
|
|
32
|
+
const context = useContext(PrivateQueryMaskContext)
|
|
31
33
|
|
|
32
34
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
33
35
|
const isSSR = process.env.NODE_ENV === 'development' ? useIsSSR() : false
|
|
@@ -35,7 +37,7 @@ export function useInContextInputMask(): InContextMaskContextType {
|
|
|
35
37
|
if (!context) {
|
|
36
38
|
if (isSSR)
|
|
37
39
|
console.warn(
|
|
38
|
-
"
|
|
40
|
+
"usePrivateQueryMask was used without a PrivateQueryMaskProvider, this means that customer specific pricing probably isn't working.",
|
|
39
41
|
)
|
|
40
42
|
|
|
41
43
|
return { mask: false }
|
|
@@ -43,31 +45,31 @@ export function useInContextInputMask(): InContextMaskContextType {
|
|
|
43
45
|
return context
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export function
|
|
48
|
+
export function PrivateQueryMaskProvider(props: { mask: boolean; children: React.ReactNode }) {
|
|
47
49
|
const { mask = false, children } = props
|
|
48
50
|
return (
|
|
49
|
-
<
|
|
51
|
+
<PrivateQueryMaskContext.Provider value={useMemo(() => ({ mask }), [mask])}>
|
|
50
52
|
{children}
|
|
51
|
-
</
|
|
53
|
+
</PrivateQueryMaskContext.Provider>
|
|
52
54
|
)
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
export function
|
|
57
|
+
export function usePrivateQueryMaskSx(props: { sx?: SxProps<Theme>; skeleton?: SkeletonProps }) {
|
|
56
58
|
const { sx = [], skeleton } = props
|
|
57
|
-
const { mask } =
|
|
59
|
+
const { mask } = usePrivateQueryMask()
|
|
58
60
|
|
|
59
61
|
return {
|
|
60
62
|
mask,
|
|
61
63
|
componentSx: [
|
|
62
64
|
mask && {
|
|
63
|
-
[cssFlag('
|
|
65
|
+
[cssFlag('private-query')]: { display: 'none' },
|
|
64
66
|
},
|
|
65
67
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
66
68
|
],
|
|
67
69
|
maskSx: [
|
|
68
70
|
{
|
|
69
71
|
display: 'inline-block',
|
|
70
|
-
[cssNotFlag('
|
|
72
|
+
[cssNotFlag('private-query')]: { display: 'none' },
|
|
71
73
|
},
|
|
72
74
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
73
75
|
...(Array.isArray(skeleton?.sx) ? skeleton.sx : [skeleton?.sx]),
|
|
@@ -75,12 +77,10 @@ export function useInContextInputMaskSx(props: { sx?: SxProps<Theme>; skeleton?:
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
*/
|
|
81
|
-
export function InContextMask(props: InContextMaskProps) {
|
|
80
|
+
/** A component that renders a skeleton mask when the user is signed in. */
|
|
81
|
+
export function PrivateQueryMask(props: PrivateQueryMaskProps) {
|
|
82
82
|
const { skeleton, children, ...rest } = props
|
|
83
|
-
const { mask, componentSx, maskSx } =
|
|
83
|
+
const { mask, componentSx, maskSx } = usePrivateQueryMaskSx(props)
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<>
|
package/config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { GraphCommerceStorefrontConfig } from '@graphcommerce/next-config'
|
|
|
2
2
|
import type { ApolloLink, TypePolicies } from '@apollo/client'
|
|
3
3
|
import type { SetRequired } from 'type-fest'
|
|
4
4
|
import type { MigrateCache } from './components/GraphQLProvider/migrateCache'
|
|
5
|
+
import { RemovePrivateContextDirectivesLink } from './link/RemovePrivateContextDirectivesLink'
|
|
5
6
|
|
|
6
7
|
export interface PreviewData {}
|
|
7
8
|
|
|
@@ -34,5 +35,11 @@ export type ApolloClientConfig = SetRequired<
|
|
|
34
35
|
|
|
35
36
|
export function graphqlConfig(config: ApolloClientConfigInput): ApolloClientConfig {
|
|
36
37
|
const { storefront, links = [], policies = [], migrations = [], ...rest } = config
|
|
37
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
storefront,
|
|
40
|
+
links: [...links, new RemovePrivateContextDirectivesLink()],
|
|
41
|
+
policies,
|
|
42
|
+
migrations,
|
|
43
|
+
...rest,
|
|
44
|
+
}
|
|
38
45
|
}
|
|
@@ -1,51 +1,54 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InputMaybe, PrivateContext } from '@graphcommerce/graphql-mesh'
|
|
2
2
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
3
3
|
import { useIsSSR } from '@graphcommerce/next-ui/hooks/useIsSsr'
|
|
4
4
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
5
5
|
import { getCssFlag, removeCssFlag, setCssFlag } from '@graphcommerce/next-ui/utils/cssFlags'
|
|
6
6
|
import { useContext, useEffect } from 'react'
|
|
7
|
-
import type { QueryHookOptions, QueryResult, TypedDocumentNode } from '../apollo'
|
|
7
|
+
import type { MaybeMasked, QueryHookOptions, QueryResult, TypedDocumentNode } from '../apollo'
|
|
8
8
|
import { useQuery } from '../apollo'
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { PrivateQueryMaskContext } from '../components/PrivateQueryMask/PrivateQueryMask'
|
|
10
|
+
import { usePrivateQueryContext } from './usePrivateQueryContext'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Creates a query that allows fetching data for logged in customers
|
|
14
|
-
* a fallback for guest users.
|
|
13
|
+
* Creates a query that allows fetching data for logged in customers (or other private contexts),
|
|
14
|
+
* but have a fallback for guest users.
|
|
15
15
|
*
|
|
16
16
|
* - Shows a global pageload indicator when loading customer specific information.
|
|
17
17
|
*
|
|
18
18
|
* When not to use this?
|
|
19
|
-
*
|
|
19
|
+
*
|
|
20
|
+
* - When a query is always scoped. This method specifically targets queries that can resolve unscoped
|
|
21
|
+
* (guest) and both scoped (customer) data.
|
|
20
22
|
*
|
|
21
23
|
* Usage:
|
|
22
|
-
*
|
|
23
|
-
* -
|
|
24
|
+
*
|
|
25
|
+
* - Define a `@privateContext(context: $context)` directive in your query
|
|
26
|
+
* - Use the usePrivateQuery
|
|
24
27
|
*/
|
|
25
|
-
export function
|
|
28
|
+
export function usePrivateQuery<
|
|
26
29
|
Q,
|
|
27
|
-
V extends { context?: InputMaybe<
|
|
30
|
+
V extends { context?: InputMaybe<PrivateContext>; [index: string]: unknown },
|
|
28
31
|
>(
|
|
29
32
|
document: TypedDocumentNode<Q, V>,
|
|
30
33
|
options: QueryHookOptions<Q, V>,
|
|
31
34
|
unscopedResult: Q,
|
|
32
|
-
): Omit<QueryResult<Q, V>, 'data'> & { data: Q
|
|
35
|
+
): Omit<QueryResult<Q, V>, 'data'> & { data: Q | NonNullable<MaybeMasked<Q>>; mask: boolean } {
|
|
33
36
|
const { skip = true } = options
|
|
34
|
-
const context =
|
|
37
|
+
const context = usePrivateQueryContext()
|
|
35
38
|
const isSsr = useIsSSR()
|
|
36
39
|
|
|
37
|
-
const
|
|
40
|
+
const privateContext = useContext(PrivateQueryMaskContext)
|
|
38
41
|
|
|
39
42
|
useEffect(() => {
|
|
40
43
|
if (isSsr) return
|
|
41
|
-
if (context && !getCssFlag('
|
|
42
|
-
else if (!context && getCssFlag('
|
|
44
|
+
if (context && !getCssFlag('private-query')) setCssFlag('private-query', true)
|
|
45
|
+
else if (!context && getCssFlag('private-query')) removeCssFlag('private-query')
|
|
43
46
|
}, [context, isSsr])
|
|
44
47
|
|
|
45
48
|
const clientQuery = useQuery<Q, V>(document, {
|
|
46
49
|
...options,
|
|
47
50
|
variables: { ...options.variables, context } as V,
|
|
48
|
-
skip: !!
|
|
51
|
+
skip: !!privateContext || (skip && !context),
|
|
49
52
|
})
|
|
50
53
|
|
|
51
54
|
let { data } = clientQuery
|
|
@@ -57,9 +60,9 @@ export function useInContextQuery<
|
|
|
57
60
|
mask = !skip ? !clientQuery.data && !clientQuery.previousData : !clientQuery.data
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
// If this method is called within an
|
|
61
|
-
if (
|
|
62
|
-
mask =
|
|
63
|
+
// If this method is called within an PrivateQueryMask, we skip this complete functionality so we show the parent mask.
|
|
64
|
+
if (privateContext) {
|
|
65
|
+
mask = privateContext.mask
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
return { ...clientQuery, data: data ?? unscopedResult, mask }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PrivateContext } from '@graphcommerce/graphql-mesh'
|
|
2
2
|
import type { ApolloClient } from '@apollo/client'
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function getPrivateQueryContext(
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
6
6
|
client: ApolloClient<any>,
|
|
7
|
-
):
|
|
7
|
+
): PrivateContext | null {
|
|
8
8
|
return null
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -15,6 +15,6 @@ export function getInContextInput(
|
|
|
15
15
|
*
|
|
16
16
|
* @see @graphcommerce/magento-customer/plugins/magentoCustomerGetInContext.ts
|
|
17
17
|
*
|
|
18
|
-
* Note: ONLY return a value if the frontend should use the
|
|
18
|
+
* Note: ONLY return a value if the frontend should use the privateContext directive.
|
|
19
19
|
*/
|
|
20
|
-
export const
|
|
20
|
+
export const usePrivateQueryContext = (): PrivateContext | null => null
|
package/index.ts
CHANGED
|
@@ -5,6 +5,6 @@ export * from './generated/types'
|
|
|
5
5
|
export * from './config'
|
|
6
6
|
export * from './utils/getPreviewData'
|
|
7
7
|
export * from './utils/cachePolicy'
|
|
8
|
-
export * from './components/
|
|
9
|
-
export * from './hooks/
|
|
10
|
-
export * from './hooks/
|
|
8
|
+
export * from './components/PrivateQueryMask/PrivateQueryMask'
|
|
9
|
+
export * from './hooks/usePrivateQueryContext'
|
|
10
|
+
export * from './hooks/usePrivateQuery'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NextLink, Operation } from '../apollo'
|
|
2
|
+
import { ApolloLink, removeArgumentsFromDocument, removeDirectivesFromDocument } from '../apollo'
|
|
3
|
+
|
|
4
|
+
export class RemovePrivateContextDirectivesLink extends ApolloLink {
|
|
5
|
+
// eslint-disable-next-line class-methods-use-this
|
|
6
|
+
request(operation: Operation, forward?: NextLink) {
|
|
7
|
+
if (!forward) throw new Error('This is not a terminal link!')
|
|
8
|
+
|
|
9
|
+
let modifiedQuery = operation.query
|
|
10
|
+
|
|
11
|
+
modifiedQuery =
|
|
12
|
+
removeDirectivesFromDocument([{ name: 'privateContext' }], modifiedQuery) ?? modifiedQuery
|
|
13
|
+
modifiedQuery =
|
|
14
|
+
removeArgumentsFromDocument([{ name: 'context' }], modifiedQuery) ?? modifiedQuery
|
|
15
|
+
|
|
16
|
+
operation.query = modifiedQuery
|
|
17
|
+
return forward(operation)
|
|
18
|
+
}
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/graphql",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "9.0.0-canary.
|
|
5
|
+
"version": "9.0.0-canary.116",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "index.ts",
|
|
8
8
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@apollo/client": "~3.
|
|
16
|
+
"@apollo/client": "~3.12.3",
|
|
17
17
|
"@graphql-codegen/add": "5.0.3",
|
|
18
18
|
"@graphql-codegen/fragment-matcher": "5.0.2",
|
|
19
19
|
"@graphql-codegen/introspection": "4.0.3",
|
|
@@ -24,14 +24,15 @@
|
|
|
24
24
|
"@graphql-codegen/typescript-document-nodes": "4.0.12",
|
|
25
25
|
"@graphql-codegen/typescript-operations": "4.4.0",
|
|
26
26
|
"apollo3-cache-persist": "^0.15.0",
|
|
27
|
-
"graphql": "^16.
|
|
27
|
+
"graphql": "^16.10.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.
|
|
31
|
-
"@graphcommerce/graphql-codegen-near-operation-file": "9.0.0-canary.
|
|
32
|
-
"@graphcommerce/graphql-codegen-relay-optimizer-plugin": "9.0.0-canary.
|
|
33
|
-
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.
|
|
34
|
-
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.
|
|
30
|
+
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.116",
|
|
31
|
+
"@graphcommerce/graphql-codegen-near-operation-file": "9.0.0-canary.116",
|
|
32
|
+
"@graphcommerce/graphql-codegen-relay-optimizer-plugin": "9.0.0-canary.116",
|
|
33
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.116",
|
|
34
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.116",
|
|
35
|
+
"@graphql-mesh/plugin-http-details-extensions": "*",
|
|
35
36
|
"react": "^18.2.0",
|
|
36
37
|
"react-dom": "^18.2.0"
|
|
37
38
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
2
|
By providing these values the Apollo Client cache will automatically scope the cache to these specific values.
|
|
3
3
|
|
|
4
|
-
If a
|
|
4
|
+
If a PrivateContext is provided
|
|
5
5
|
"""
|
|
6
|
-
input
|
|
6
|
+
input PrivateContext {
|
|
7
7
|
loggedIn: Boolean
|
|
8
8
|
|
|
9
9
|
# storeCode: String
|
|
@@ -13,4 +13,4 @@ input InContextInput {
|
|
|
13
13
|
"""
|
|
14
14
|
Certain values are only available in the context of a session and need to have the cache scoped to these values.
|
|
15
15
|
"""
|
|
16
|
-
directive @
|
|
16
|
+
directive @privateContext(context: PrivateContext) on FIELD
|