feat: NFT GraphQL Feature Flag (#4969)

* add nft graphQl feature flag

* connect flag to env provider

* attempting metadata relay env switch

* working config

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
This commit is contained in:
Charles Bachmeier 2022-10-20 08:51:00 -07:00 committed by GitHub
parent 5f431a1e26
commit a21bbfd5a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 39 additions and 12 deletions

@ -1,5 +1,6 @@
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags' import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft' import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc' import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react' import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
@ -204,6 +205,12 @@ export default function FeatureFlagModal() {
</Header> </Header>
<FeatureFlagGroup name="Phase 1"> <FeatureFlagGroup name="Phase 1">
<FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" /> <FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
<FeatureFlagOption
variant={NftGraphQlVariant}
value={useNftGraphQlFlag()}
featureFlag={FeatureFlag.nftGraphQl}
label="NFT GraphQL Endpoints"
/>
</FeatureFlagGroup> </FeatureFlagGroup>
<FeatureFlagGroup name="Debug"> <FeatureFlagGroup name="Debug">
<FeatureFlagOption <FeatureFlagOption

@ -3,4 +3,5 @@ export enum FeatureFlag {
nft = 'nfts', nft = 'nfts',
traceJsonRpc = 'traceJsonRpc', traceJsonRpc = 'traceJsonRpc',
multiNetworkBalances = 'multiNetworkBalances', multiNetworkBalances = 'multiNetworkBalances',
nftGraphQl = 'nftGraphQl',
} }

@ -0,0 +1,7 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useNftGraphQlFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.nftGraphQl)
}
export { BaseVariant as NftGraphQlVariant }

@ -1,41 +1,40 @@
import ms from 'ms.macro' import ms from 'ms.macro'
import { Variables } from 'react-relay' import { Variables } from 'react-relay'
import { Environment, Network, RecordSource, RequestParameters, Store } from 'relay-runtime' import { CacheConfig, Environment, Network, RecordSource, RequestParameters, Store } from 'relay-runtime'
import RelayQueryResponseCache from 'relay-runtime/lib/network/RelayQueryResponseCache' import RelayQueryResponseCache from 'relay-runtime/lib/network/RelayQueryResponseCache'
import fetchGraphQL from './fetchGraphQL' import fetchGraphQL from './fetchGraphQL'
// max number of request in cache, least-recently updated entries purged first // max number of request in cache, least-recently updated entries purged first
const size = 250 const size = 250
// number in milliseconds, how long records stay valid in cache // number in milliseconds, how long records stay valid in cache
const ttl = ms`5m` const ttl = ms`5m`
export const cache = new RelayQueryResponseCache({ size, ttl }) export const cache = new RelayQueryResponseCache({ size, ttl })
const fetchQuery = async function wrappedFetchQuery(params: RequestParameters, variables: Variables) { const fetchQuery = async function wrappedFetchQuery(
params: RequestParameters,
variables: Variables,
cacheConfig: CacheConfig
) {
const queryID = params.name const queryID = params.name
const cachedData = cache.get(queryID, variables) const cachedData = cache.get(queryID, variables)
if (cachedData !== null) return cachedData if (cachedData !== null) return cachedData
return fetchGraphQL(params, variables).then((data) => { return fetchGraphQL(params, variables, cacheConfig).then((data) => {
if (params.operationKind !== 'mutation') { if (params.operationKind !== 'mutation') {
cache.set(queryID, variables, data) cache.set(queryID, variables, data)
} }
return data return data
}) })
} }
// This property tells Relay to not immediately clear its cache when the user // This property tells Relay to not immediately clear its cache when the user
// navigates around the app. Relay will hold onto the specified number of // navigates around the app. Relay will hold onto the specified number of
// query results, allowing the user to return to recently visited pages // query results, allowing the user to return to recently visited pages
// and reusing cached data if its available/fresh. // and reusing cached data if its available/fresh.
const gcReleaseBufferSize = 10 const gcReleaseBufferSize = 10
const queryCacheExpirationTime = ms`1m` const queryCacheExpirationTime = ms`1m`
const store = new Store(new RecordSource(), { gcReleaseBufferSize, queryCacheExpirationTime }) const store = new Store(new RecordSource(), { gcReleaseBufferSize, queryCacheExpirationTime })
const network = Network.create(fetchQuery) const network = Network.create(fetchQuery)
// Export a singleton instance of Relay Environment configured with our network function: // Export a singleton instance of Relay Environment configured with our network function:
export default new Environment({ export default new Environment({
network, network,

@ -1,22 +1,35 @@
import { Variables } from 'react-relay' import { Variables } from 'react-relay'
import { GraphQLResponse, RequestParameters } from 'relay-runtime' import { CacheConfig, GraphQLResponse, RequestParameters } from 'relay-runtime'
const URL = process.env.REACT_APP_AWS_API_ENDPOINT const URL = process.env.REACT_APP_AWS_API_ENDPOINT
const NFT_URL = process.env.REACT_APP_NFT_AWS_API_ENDPOINT ?? ''
if (!URL) { if (!URL) {
throw new Error('AWS URL MISSING FROM ENVIRONMENT') throw new Error('AWS URL MISSING FROM ENVIRONMENT')
} }
const headers = { const baseHeaders = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
const fetchQuery = (params: RequestParameters, variables: Variables): Promise<GraphQLResponse> => { const nftHeaders = {
'Content-Type': 'application/json',
'x-api-key': process.env.REACT_APP_NFT_AWS_X_API_KEY ?? '',
}
const fetchQuery = (
params: RequestParameters,
variables: Variables,
cacheConfig: CacheConfig
): Promise<GraphQLResponse> => {
const { metadata: { isNFT } = { isNFT: false } } = cacheConfig
const body = JSON.stringify({ const body = JSON.stringify({
query: params.text, // GraphQL text from input query: params.text, // GraphQL text from input
variables, variables,
}) })
const url = isNFT ? NFT_URL : URL
const headers = isNFT ? nftHeaders : baseHeaders
return fetch(URL, { method: 'POST', body, headers }) return fetch(url, { method: 'POST', body, headers })
.then((res) => res.json()) .then((res) => res.json())
.catch((e) => { .catch((e) => {
console.error(e) console.error(e)