Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add defer directive to the operations stats page #2082

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fa40e23
Use defer for projects list
beerose Mar 28, 2023
517dae5
try defer in operations page
beerose Apr 5, 2023
eb3d2ad
Do not consume responses but forward
ardatan Apr 6, 2023
9591a23
Do not consume responses but forward
ardatan Apr 6, 2023
3f5149f
Add defer to operations stats
beerose Apr 6, 2023
0e14842
revert type change
beerose Apr 6, 2023
3f13687
Simplify prop
beerose Apr 6, 2023
49e3fb2
Merge branch 'main' into add-defer
beerose Apr 6, 2023
d411589
Merge branch 'main' into add-defer
beerose Apr 11, 2023
e81bc04
Patch urql to handle multipart/mixed as default
beerose Apr 12, 2023
6ed4072
Update yoga
beerose Apr 13, 2023
83ce7b9
Make it work temp
beerose Apr 13, 2023
173d5a2
minor changes
beerose Apr 18, 2023
3e300c5
Upgrade urql
beerose Apr 20, 2023
38f54ed
Merge branch 'main' into add-defer
beerose Apr 20, 2023
c9f8971
Remove urql devtools from the client setup
beerose Apr 20, 2023
80e6c3a
Updates after upgrading urql
beerose Apr 20, 2023
0569304
Add defer directive to schema
beerose Apr 23, 2023
61cece0
move defer directive definition to shared schema
beerose Apr 24, 2023
b159705
Merge branch 'main' into add-defer
beerose Apr 24, 2023
02025b9
Fix import
beerose Apr 24, 2023
61835d1
Merge branch 'main' into add-defer
beerose Apr 25, 2023
c6a91d6
Run prettier
beerose Apr 25, 2023
3388ac6
Update packages/web/app/src/lib/urql-cache.ts
beerose Apr 25, 2023
68decbe
Merge branch 'main' into add-defer
kamilkisiela May 9, 2023
5ecf750
Defer Organization.getStarted
kamilkisiela May 10, 2023
9a86652
Merge branch 'main' into add-defer
kamilkisiela May 10, 2023
99079b3
test hive with new rc
beerose May 17, 2023
6c05873
Merge branch 'main' into add-defer
beerose May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.json
Expand Up @@ -51,13 +51,13 @@
"@changesets/changelog-github": "0.4.8",
"@changesets/cli": "2.26.1",
"@graphql-codegen/add": "4.0.1",
"@graphql-codegen/cli": "3.3.1",
"@graphql-codegen/client-preset": "3.0.1",
"@graphql-codegen/graphql-modules-preset": "3.1.3",
"@graphql-codegen/typed-document-node": "4.0.1",
"@graphql-codegen/typescript": "3.0.4",
"@graphql-codegen/typescript-operations": "3.0.4",
"@graphql-codegen/typescript-resolvers": "3.2.1",
"@graphql-codegen/cli": "3.3.1-alpha-20230420202418-82620d460",
"@graphql-codegen/client-preset": "3.0.1-alpha-20230420202418-82620d460",
"@graphql-codegen/graphql-modules-preset": "3.1.3-alpha-20230420202418-82620d460",
"@graphql-codegen/typed-document-node": "4.0.1-alpha-20230420202418-82620d460",
"@graphql-codegen/typescript": "3.0.4-alpha-20230420202418-82620d460",
"@graphql-codegen/typescript-operations": "3.1.0-alpha-20230420202418-82620d460",
"@graphql-codegen/typescript-resolvers": "3.2.1-alpha-20230420202418-82620d460",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: update after codegen is released

"@graphql-inspector/cli": "3.4.16",
"@manypkg/get-packages": "2.1.0",
"@next/eslint-plugin-next": "13.3.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/services/api/src/modules/shared/module.graphql.ts
@@ -1,6 +1,8 @@
import { gql } from 'graphql-modules';

export default gql`
directive @defer on FRAGMENT_SPREAD | INLINE_FRAGMENT

scalar DateTime
scalar JSON
scalar SafeInt
Expand Down
1 change: 1 addition & 0 deletions packages/services/server/package.json
Expand Up @@ -19,6 +19,7 @@
"@escape.tech/graphql-armor-max-depth": "1.8.4",
"@escape.tech/graphql-armor-max-directives": "1.6.5",
"@escape.tech/graphql-armor-max-tokens": "1.3.2",
"@graphql-yoga/plugin-defer-stream": "1.9.1",
"@sentry/integrations": "7.49.0",
"@sentry/node": "7.49.0",
"@trpc/server": "10.21.1",
Expand Down
7 changes: 4 additions & 3 deletions packages/services/server/src/graphql-handler.ts
Expand Up @@ -12,6 +12,7 @@ import { useGenericAuth } from '@envelop/generic-auth';
import { useGraphQLModules } from '@envelop/graphql-modules';
import { useSentry } from '@envelop/sentry';
import { useHive } from '@graphql-hive/client';
import { useDeferStream } from '@graphql-yoga/plugin-defer-stream';
import { Registry, RegistryContext } from '@hive/api';
import { HiveError } from '@hive/api';
import { cleanRequestId } from '@hive/service-common';
Expand Down Expand Up @@ -93,6 +94,7 @@ export const graphqlHandler = (options: GraphQLHandlerOptions): RouteHandlerMeth
const server = createYoga<Context>({
logging: options.logger,
plugins: [
useDeferStream(),
useArmor(),
useSentry({
startTransaction: false,
Expand Down Expand Up @@ -259,14 +261,13 @@ export const graphqlHandler = (options: GraphQLHandlerOptions): RouteHandlerMeth

const accept = req.headers.accept;

if (!accept || accept === '*/*') {
if (!accept || accept === '*/*' || !reply.hasHeader('content-type')) {
void reply.header('content-type', 'application/json');
}

void reply.status(response.status);

const textResponse = await response.text();
return reply.send(textResponse);
void reply.send(response.body || '');
},
);
};
Expand Down
4 changes: 2 additions & 2 deletions packages/web/app/package.json
Expand Up @@ -42,7 +42,7 @@
"@theguild/editor": "1.2.5",
"@trpc/client": "10.21.1",
"@trpc/server": "10.21.1",
"@urql/core": "3.1.1",
"@urql/core": "4.0.6",
"@urql/devtools": "2.0.3",
"@urql/exchange-graphcache": "5.0.9",
"@whatwg-node/fetch": "0.8.6",
Expand Down Expand Up @@ -79,7 +79,7 @@
"supertokens-node": "13.5.0",
"supertokens-web-js": "0.5.0",
"tslib": "2.5.0",
"urql": "3.0.3",
"urql": "4.0.0",
"use-debounce": "9.0.4",
"valtio": "1.10.4",
"wonka": "6.3.1",
Expand Down
1 change: 0 additions & 1 deletion packages/web/app/pages/[orgId]/index.tsx
Expand Up @@ -165,7 +165,6 @@ function ProjectsPage(): ReactElement {
/>
) : (
<div className="grid grid-cols-2 gap-5 items-stretch">
{/** TODO: use defer here :) */}
{projects === null
? [1, 2].map(key => (
<Card key={key}>
Expand Down
50 changes: 40 additions & 10 deletions packages/web/app/pages/api/proxy.ts
Expand Up @@ -3,6 +3,7 @@ import hyperid from 'hyperid';
import { env } from '@/env/backend';
import { extractAccessTokenFromRequest } from '@/lib/api/extract-access-token-from-request';
import { captureException, getCurrentHub, wrapApiHandlerWithSentry } from '@sentry/nextjs';
import { fetch } from '@whatwg-node/fetch';

const reqIdGenerate = hyperid({ fixedLength: true });
async function graphql(req: NextApiRequest, res: NextApiResponse) {
Expand All @@ -29,7 +30,23 @@ async function graphql(req: NextApiRequest, res: NextApiResponse) {
},
method: 'GET',
} as any);
return res.send(await response.text());

res.writeHead(response.status, Object.fromEntries(response.headers.entries()));

if (response.body) {
const reader = response.body.getReader();
let streamDone = false;
while (!streamDone) {
const { done, value } = await reader.read();
if (done) {
streamDone = true;
} else {
res.write(Buffer.from(value));
}
}
}

return res.end();
}

const scope = getCurrentHub().getScope();
Expand Down Expand Up @@ -77,7 +94,7 @@ async function graphql(req: NextApiRequest, res: NextApiResponse) {
headers: {
Authorization: `Bearer ${accessToken}`,
'content-type': req.headers['content-type'],
accept: req.headers['accept'],
accept: req.headers['accept'] + ', multipart/mixed',
'accept-encoding': req.headers['accept-encoding'],
'x-request-id': requestId,
'X-API-Token': req.headers['x-api-token'] ?? '',
Expand All @@ -88,16 +105,32 @@ async function graphql(req: NextApiRequest, res: NextApiResponse) {
body: JSON.stringify(req.body || {}),
} as any);

graphqlSpan?.setHttpStatus(200);
graphqlSpan?.finish();

const resHeaders = Object.fromEntries(response.headers.entries());

const xRequestId = response.headers.get('x-request-id');
if (xRequestId) {
res.setHeader('x-request-id', xRequestId);
resHeaders['x-request-id'] = xRequestId;
}
const parsedData = await response.json();

graphqlSpan?.setHttpStatus(200);
graphqlSpan?.finish();
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));

if (response.body) {
const reader = response.body.getReader();
let streamDone = false;
while (!streamDone) {
const { done, value } = await reader.read();
if (done) {
streamDone = true;
} else {
res.write(Buffer.from(value));
}
}
}

res.status(200).json(parsedData);
return res.end();
} catch (error) {
console.error(error);
captureException(error);
Expand All @@ -121,9 +154,6 @@ export default wrapApiHandlerWithSentry(graphql, 'api/proxy');

export const config = {
api: {
bodyParser: {
sizeLimit: '6mb',
},
externalResolver: true,
},
};
9 changes: 5 additions & 4 deletions packages/web/app/src/components/target/operations/Stats.tsx
Expand Up @@ -340,10 +340,11 @@ function OverTimeStats({
}

function ClientsStats({
clients = [],
data,
}: {
clients?: GeneralOperationsStatsQuery['operationsStats']['clients']['nodes'];
data: GeneralOperationsStatsQuery['operationsStats']['clients'] | null;
}): ReactElement {
const clients = data?.nodes;
const styles = useChartStyles();
const sortedClients = useMemo(() => {
return clients?.length ? clients.slice().sort((a, b) => b.count - a.count) : [];
Expand Down Expand Up @@ -755,7 +756,7 @@ export function OperationsStats({
});
}, [refetchQuery]);

const isFetching = query.fetching;
const isFetching = query.fetching || query.stale;
const isError = !!query.error;

const operationsStats = query.data?.operationsStats;
Expand All @@ -781,7 +782,7 @@ export function OperationsStats({
</div>
</OperationsFallback>
<div>
<ClientsStats clients={operationsStats?.clients?.nodes} />
<ClientsStats data={operationsStats?.clients ?? null} />
</div>
<div>
<OperationsFallback isError={isError} refetch={refetch} isFetching={isFetching}>
Expand Down
Expand Up @@ -11,7 +11,7 @@ query generalOperationsStats($selector: OperationsStatsSelectorInput!, $resoluti
p99
}
}
... on OperationsStats {
... on OperationsStats @defer {
failuresOverTime(resolution: $resolution) {
date
value
Expand All @@ -30,7 +30,7 @@ query generalOperationsStats($selector: OperationsStatsSelectorInput!, $resoluti
}
}
}
... on OperationsStats {
... on OperationsStats @defer {
clients {
nodes {
name
Expand Down
11 changes: 10 additions & 1 deletion packages/web/app/src/lib/urql-cache.ts
@@ -1,6 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { DocumentNode, Kind } from 'graphql';
import { produce } from 'immer';
import { getOperationName, TypedDocumentNode } from 'urql';
import { TypedDocumentNode } from 'urql';
import { ResultOf, VariablesOf } from '@graphql-typed-document-node/core';
import { Cache, QueryInput, UpdateResolver } from '@urql/exchange-graphcache';
import {
Expand Down Expand Up @@ -28,6 +29,14 @@ import {
TokensDocument,
} from '../graphql';

export const getOperationName = (query: DocumentNode): string | undefined => {
for (const node of query.definitions) {
if (node.kind === Kind.OPERATION_DEFINITION) {
return node.name ? node.name.value : undefined;
beerose marked this conversation as resolved.
Show resolved Hide resolved
}
}
};

function updateQuery<T, V>(cache: Cache, input: QueryInput<T, V>, recipe: (obj: T) => void) {
return cache.updateQuery(input, (data: T | null) => {
if (!data) {
Expand Down
3 changes: 1 addition & 2 deletions packages/web/app/src/lib/urql.ts
@@ -1,4 +1,4 @@
import { createClient, dedupExchange, errorExchange, fetchExchange } from 'urql';
import { createClient, errorExchange, fetchExchange } from 'urql';
import { cacheExchange } from '@urql/exchange-graphcache';
import { Mutation } from './urql-cache';
import { networkStatusExchange } from './urql-exchanges/state';
Expand All @@ -10,7 +10,6 @@ const SERVER_BASE_PATH = '/api/proxy';
export const urqlClient = createClient({
url: SERVER_BASE_PATH,
exchanges: [
dedupExchange,
cacheExchange({
updates: {
Mutation,
Expand Down