Skip to content

Commit

Permalink
fix: tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vicary committed Mar 10, 2023
1 parent 5c1471a commit 13dca64
Show file tree
Hide file tree
Showing 22 changed files with 202 additions and 245 deletions.
4 changes: 2 additions & 2 deletions examples/ez/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
"gqty": "workspace:^2.3.0",
"graphql": "^16.6.0",
"graphql-ez": "^0.16.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"randomstring": "^1.2.3",
"test-utils": "workspace:^0.1.0"
},
"devDependencies": {
"@types/lodash": "^4.14.191",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.14.6",
"@types/randomstring": "^1.1.8",
"bob-tsm": "^1.1.2",
Expand Down
46 changes: 24 additions & 22 deletions examples/ez/src/generated/gqty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,48 @@ import {
GeneratedSchema,
generatedSchema,
scalarsEnumsHash,
SchemaObjectTypes,
SchemaObjectTypesNames,
} from './schema.generated';

import { CreateTestClient } from '@graphql-ez/fastify-testing';

const testClientPromise = CreateTestClient(ezApp);
const queryFetcher: QueryFetcher = async function (query, variables) {
const queryFetcher: QueryFetcher = async function ({
query,
variables,
operationName,
}) {
const testClient = await testClientPromise;

return testClient.query(query, {
variables,
operationName,
});
};

export const client = createClient<
GeneratedSchema,
SchemaObjectTypesNames,
SchemaObjectTypes
>({
export const client = createClient<GeneratedSchema>({
schema: generatedSchema,
scalarsEnumsHash,
queryFetcher,
normalization: {
identifier(obj) {
switch (obj.__typename) {
case 'A': {
return obj.a;
scalars: scalarsEnumsHash,
cacheOptions: {
normalization: {
identity(obj) {
switch (obj.__typename) {
case 'A': {
return `${obj.a ?? ''}`;
}
default: {
return;
}
}
default: {
return;
}
}
},
schemaKeys: {},
},
keyFields: {},
},
fetchOptions: {
fetcher: queryFetcher,
},
});

const { query, mutation, mutate, subscription, resolved, refetch } = client;

export { query, mutation, mutate, subscription, resolved, refetch };

export * from './schema.generated';
export { query, mutation, mutate, subscription, resolved, refetch };
20 changes: 0 additions & 20 deletions examples/ez/src/generated/schema.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,26 +212,6 @@ export interface Subscription {
__typename?: 'Subscription';
}

export interface SchemaObjectTypes {
A: A;
B: B;
C: C;
Dog: Dog;
Human: Human;
Mutation: Mutation;
Query: Query;
Subscription: Subscription;
}
export type SchemaObjectTypesNames =
| 'A'
| 'B'
| 'C'
| 'Dog'
| 'Human'
| 'Mutation'
| 'Query'
| 'Subscription';

export interface $NamedEntity {
Dog?: Dog;
Human?: Human;
Expand Down
2 changes: 1 addition & 1 deletion examples/ez/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ezAltairIDE } from '@graphql-ez/plugin-altair/static';
import { ezCodegen } from '@graphql-ez/plugin-codegen';
import { ezSchema } from '@graphql-ez/plugin-schema';
import Fastify, { LogLevel } from 'fastify';
import { random, range } from 'lodash';
import { random, range } from 'lodash-es';
import { generate } from 'randomstring';
import { gql } from 'test-utils';
import { GreetingsEnum, Human, TestUnion } from './ez.generated';
Expand Down
19 changes: 8 additions & 11 deletions examples/ez/test/example.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { waitForExpect } from 'test-utils';

import { CreateTestClient, GlobalTeardown } from '@graphql-ez/fastify-testing';
import { selectFields } from 'gqty';

import { waitForExpect } from 'test-utils';
import { ezApp } from '../src';
import {
ArrayObjectArgsDocument,
MultipleArgsDocument,
SimpleStringDocument,
} from '../src/ez.generated';
import {
client as generatedClient,
GreetingsEnum,
mutation,
query,
resolved,
} from '../src/generated/gqty';
import {
ArrayObjectArgsDocument,
MultipleArgsDocument,
SimpleStringDocument,
} from '../src/ez.generated';

import { CreateTestClient, GlobalTeardown } from '@graphql-ez/fastify-testing';
import { ezApp } from '../src';

const testClientPromise = CreateTestClient(ezApp);

Expand Down
23 changes: 8 additions & 15 deletions integration/graphql-17/src/gqty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,25 @@
import type { QueryFetcher } from 'gqty';
import { createClient } from 'gqty';
import { TestClient } from './api';
import type {
GeneratedSchema,
SchemaObjectTypes,
SchemaObjectTypesNames,
} from './schema.generated';
import type { GeneratedSchema } from './schema.generated';
import { generatedSchema, scalarsEnumsHash } from './schema.generated';

const queryFetcher: QueryFetcher = async function (query, variables) {
const queryFetcher: QueryFetcher = async function ({ query, variables }) {
return (await TestClient).query(query, {
variables,
});
};

export const client = createClient<
GeneratedSchema,
SchemaObjectTypesNames,
SchemaObjectTypes
>({
export const client = createClient<GeneratedSchema>({
schema: generatedSchema,
scalarsEnumsHash,
queryFetcher,
scalars: scalarsEnumsHash,
fetchOptions: {
fetcher: queryFetcher,
},
});

const { query, mutation, mutate, subscription, resolved, refetch, track } =
client;

export { query, mutation, mutate, subscription, resolved, refetch, track };

export * from './schema.generated';
export { query, mutation, mutate, subscription, resolved, refetch, track };
45 changes: 16 additions & 29 deletions packages/cli/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -733,23 +733,6 @@ export async function generate(
return acum;
}, '');

const objectTypesEntries = deps.sortBy(
Array.from(objectTypeTSTypes.entries()),
(v) => v[0]
);

typescriptTypes += `
export interface SchemaObjectTypes {
${objectTypesEntries.reduce((acum, [typeName]) => {
acum += `${typeName}:${typeName};`;
return acum;
}, '')}
}
export type SchemaObjectTypesNames = ${objectTypesEntries
.map(([key]) => `"${key}"`)
.join(' | ')};
`;

if (unionsAndInterfacesObjectTypesMap.size) {
typescriptTypes += `
${deps
Expand Down Expand Up @@ -798,8 +781,8 @@ export async function generate(
${
isJavascriptOutput
? typeDoc('import("gqty").QueryFetcher') + 'const queryFetcher'
: 'const queryFetcher : QueryFetcher'
} = async function (query, variables, fetchOptions) {
: 'const queryFetcher: QueryFetcher'
} = async function ({ query, variables, operationName }, fetchOptions) {
// Modify "${endpoint}" if needed
const response = await fetch("${endpoint}", {
method: "POST",
Expand All @@ -809,6 +792,7 @@ export async function generate(
body: JSON.stringify({
query,
variables,
operationName,
}),
mode: "cors",
...fetchOptions
Expand Down Expand Up @@ -977,15 +961,15 @@ export const generatedSchema = {${generatedSchemaCodeString}};
${react ? `import { createReactClient } from "@gqty/react"` : ''}
${
subscriptions
? `import { createSubscriptionsClient } from "@gqty/subscriptions"`
? `import { createClient as createSubscriptionsClient } from "graphql-ws"`
: ''
}
${isJavascriptOutput ? '' : 'import type { QueryFetcher } from "gqty";'}
import { createClient } from "gqty";
${
isJavascriptOutput
? ''
: 'import type { GeneratedSchema, SchemaObjectTypes, SchemaObjectTypesNames } from "./schema.generated";'
: 'import type { GeneratedSchema } from "./schema.generated";'
}
import { generatedSchema, scalarsEnumsHash } from "./schema.generated";
Expand Down Expand Up @@ -1015,19 +999,22 @@ export const generatedSchema = {${generatedSchemaCodeString}};
'import("gqty").GQtyClient<import("./schema.generated").GeneratedSchema>'
)}export const client = createClient({
schema: generatedSchema,
scalarsEnumsHash,
queryFetcher
${subscriptions ? ', subscriptionsClient' : ''}
scalars: scalarsEnumsHash,
fetchOptions: {
fetcher: queryFetcher,
${subscriptions ? 'subscriber: subscriptionsClient' : ''}
},
});`
: `export const client = createClient<GeneratedSchema, SchemaObjectTypesNames, SchemaObjectTypes>({
: `export const client = createClient<GeneratedSchema>({
schema: generatedSchema,
scalarsEnumsHash,
queryFetcher
${subscriptions ? ', subscriptionsClient' : ''}
scalars: scalarsEnumsHash,
fetchOptions:{
fetcher: queryFetcher,
${subscriptions ? 'subscriber: subscriptionsClient' : ''}
},
});`
}
const { query, mutation, mutate, subscription, resolved, refetch, track } = client;
export { query, mutation, mutate, subscription, resolved, refetch, track };
Expand Down
1 change: 1 addition & 0 deletions packages/gqty/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
},
"dependencies": {
"flatted": "^3.2.7",
"just-extend": "^6.2.0",
"just-has": "^2.3.0",
"just-memoize": "^2.2.0",
"just-safe-get": "^4.2.0",
Expand Down
39 changes: 8 additions & 31 deletions packages/gqty/src/Accessor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ export function createSchemaAccessor<TSchema extends BaseGeneratedSchema>(

if (!Reflect.has(target, key)) return;

const __typename = target[key]?.__typename;
if (!__typename || !context.schema[key]) return;
const schemaKey = key as keyof TSchema;

if (
!Reflect.get(target[schemaKey] as object, '__typename') ||
!context.schema[key]
)
return;

// Reuse root selections and their internally cached children, accessors
// can in turn be safely cached by selections but still scoped.
Expand All @@ -40,7 +45,7 @@ export function createSchemaAccessor<TSchema extends BaseGeneratedSchema>(
return createObjectAccessor({
context,
cache: {
data: target[key],
data: target[key as keyof BaseGeneratedSchema],
expiresAt: Infinity,
},
selection,
Expand Down Expand Up @@ -131,34 +136,6 @@ export const assignSelections = <TData extends GeneratedSchemaObject>(
}
};

/* TODO: Selection - null
*
* Cache accessor and selections such that subsequent selections are
* retained when null types are returned from the cache, where new selections
* are prevented from happening.
*
* Make sure such cache is cleared when new selections can be made again.
*
* Triggering onSelect() for all scalar selections inside would suffice, no
* need to cache the whole selection tree.
*
* Cache by value, nullObjectKey? Every single fetch should cache selections
* from last time, cached selections are only used as long as we got nulls.
*
* Caching accessors may prevent accessors from showing new values, so we only
* cache selections by null values and empty arrays.
*/

/* TODO: Selection - Conditional Rendering
*
* Handles conditional rendering that triggers query update on itself
* which results in infinite circular renderings.
*
* When a cache is still fresh, subsequent fetches should merge with objects
* instead of replacing them. Except on refetches, i.e. no-cache and no-store,
* which should instead invalidate reachable cache roots during selection.
*/

/* TODO: Selection - use()
*
* Replace `assignSelection` with `Selection.use(Selection)`,
Expand Down
24 changes: 24 additions & 0 deletions packages/gqty/src/Accessor/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import get from 'just-safe-get';
import set from 'just-safe-set';
import type { CacheObject } from '../Cache';
import { flattenObject } from '../Cache/crawl';
import { isCacheObject } from '../Cache/utils';
Expand All @@ -9,6 +11,7 @@ import {
Type,
} from '../Schema';
import type { Selection } from '../Selection';
import { deepAssign, isObject } from '../Utils';
import type { Meta } from './meta';
import { $meta, $setMeta } from './meta';
import { createSkeleton, isSkeleton } from './skeleton';
Expand Down Expand Up @@ -137,6 +140,27 @@ export const resolve = (

cache.data = data;

// Subscribe to cache changes at root level, child accessors should have new
// values reflected after cache updates. Listeners created this way have no
// way to be garbage collected, but the number of accessed root keys are
// assumed to be manageable.
if (selection.parent === selection.root) {
const listenerCacheKey = `__accessorCacheListeners.${cacheKeys.join('/')}`;

get(context, listenerCacheKey)?.();

const unsubscribe = context.cache.subscribe(
[cacheKeys.join('.')],
(cache) => {
if (isObject(cache) && isObject(data)) {
deepAssign(data, [get(cache, cacheKeys.join('.'))]);
}
}
);

set(context, listenerCacheKey, unsubscribe);
}

return isArray && !isNumericSelection
? createArrayAccessor({
cache,
Expand Down
Loading

0 comments on commit 13dca64

Please sign in to comment.