refactor(infra): directory structure (#4615)

This commit is contained in:
Joooye_34
2023-10-18 23:30:08 +08:00
committed by GitHub
parent 814d552be8
commit bed9310519
1150 changed files with 539 additions and 584 deletions
@@ -0,0 +1,159 @@
import { nanoid } from 'nanoid';
import type { Mock } from 'vitest';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { gqlFetcherFactory } from '../fetcher';
import type { GraphQLQuery } from '../graphql';
import {
generateRandUTF16Chars,
SPAN_ID_BYTES,
TRACE_ID_BYTES,
TraceReporter,
} from '../utils';
const query: GraphQLQuery = {
id: 'query',
query: 'query { field }',
operationName: 'query',
definitionName: 'query',
};
let fetch: Mock;
describe('GraphQL fetcher', () => {
beforeEach(() => {
fetch = vi.fn(() =>
Promise.resolve(
new Response(JSON.stringify({ data: { field: 1 } }), {
headers: {
'content-type': 'application/json',
},
})
)
);
vi.stubGlobal('fetch', fetch);
});
afterEach(() => {
fetch.mockReset();
});
const gql = gqlFetcherFactory('https://example.com/graphql');
it('should send POST request to given endpoint', async () => {
await gql(
// @ts-expect-error variables is actually optional
{ query }
);
expect(fetch).toBeCalledTimes(1);
expect(fetch.mock.lastCall[0]).toBe('https://example.com/graphql');
const ctx = fetch.mock.lastCall[1] as RequestInit;
expect(ctx.method).toBe('POST');
});
it('should send with correct graphql JSON body', async () => {
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: 1, b: '2', c: { d: false } },
});
expect(fetch.mock.lastCall[1]).toEqual(
expect.objectContaining({
body: '{"query":"query { field }","variables":{"a":1,"b":"2","c":{"d":false}},"operationName":"query"}',
headers: expect.objectContaining({
'content-type': 'application/json',
'x-definition-name': 'query',
'x-operation-name': 'query',
'x-request-id': expect.any(String),
}),
method: 'POST',
})
);
});
it('should correctly ignore nil variables', async () => {
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: false, b: null, c: undefined },
});
expect(fetch.mock.lastCall[1].body).toMatchInlineSnapshot(
'"{\\"query\\":\\"query { field }\\",\\"variables\\":{\\"a\\":false,\\"b\\":null},\\"operationName\\":\\"query\\"}"'
);
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: false, b: null, c: undefined },
keepNilVariables: false,
});
expect(fetch.mock.lastCall[1].body).toMatchInlineSnapshot(
'"{\\"query\\":\\"query { field }\\",\\"variables\\":{\\"a\\":false},\\"operationName\\":\\"query\\"}"'
);
});
it('should correct handle graphql error', async () => {
fetch.mockResolvedValue(
new Response(
JSON.stringify({
data: null,
errors: [{ message: 'error', path: ['field'] }],
}),
{
headers: {
'content-type': 'application/json',
},
status: 400,
}
)
);
await expect(gql({ query, variables: void 0 })).rejects
.toMatchInlineSnapshot(`
[
[GraphQLError: error],
]
`);
});
});
describe('Trace Reporter', () => {
const startTime = new Date().toISOString();
const traceId = generateRandUTF16Chars(TRACE_ID_BYTES);
const spanId = generateRandUTF16Chars(SPAN_ID_BYTES);
const requestId = nanoid();
it('spanId, traceId should be right format', () => {
expect(
new RegExp(`^[0-9a-f]{${SPAN_ID_BYTES * 2}}$`).test(
generateRandUTF16Chars(SPAN_ID_BYTES)
)
).toBe(true);
expect(
new RegExp(`^[0-9a-f]{${TRACE_ID_BYTES * 2}}$`).test(
generateRandUTF16Chars(TRACE_ID_BYTES)
)
).toBe(true);
});
it('test createTraceSpan', () => {
const traceSpan = TraceReporter.createTraceSpan(
traceId,
spanId,
startTime,
{ requestId }
);
expect(traceSpan.startTime).toBe(startTime);
expect(
traceSpan.name ===
`projects/{GCP_PROJECT_ID}/traces/${traceId}/spans/${spanId}`
).toBe(true);
expect(traceSpan.spanId).toBe(spanId);
expect(traceSpan.attributes.attributeMap.requestId?.stringValue.value).toBe(
requestId
);
});
});
+260
View File
@@ -0,0 +1,260 @@
import type { ExecutionResult } from 'graphql';
import { GraphQLError } from 'graphql';
import { isNil, isObject, merge } from 'lodash-es';
import { nanoid } from 'nanoid';
import type { GraphQLQuery } from './graphql';
import type { Mutations, Queries } from './schema';
import {
generateRandUTF16Chars,
SPAN_ID_BYTES,
TRACE_FLAG,
TRACE_ID_BYTES,
TRACE_VERSION,
traceReporter,
} from './utils';
export type NotArray<T> = T extends Array<unknown> ? never : T;
export type _QueryVariables<Q extends GraphQLQuery> =
Q['id'] extends Queries['name']
? Extract<Queries, { name: Q['id'] }>['variables']
: Q['id'] extends Mutations['name']
? Extract<Mutations, { name: Q['id'] }>['variables']
: undefined;
export type QueryVariables<Q extends GraphQLQuery> = _QueryVariables<Q> extends
| never
| Record<string, never>
? never
: _QueryVariables<Q>;
export type QueryResponse<Q extends GraphQLQuery> = Extract<
Queries | Mutations,
{ name: Q['id'] }
>['response'];
type NullableKeys<T> = {
[K in keyof T]: null extends T[K] ? K : never;
}[keyof T];
type NonNullableKeys<T> = {
[K in keyof T]: null extends T[K] ? never : K;
}[keyof T];
export type RecursiveMaybeFields<T> = T extends
| number
| boolean
| string
| null
| undefined
? T
: {
[K in NullableKeys<T>]?: RecursiveMaybeFields<T[K]>;
} & {
[K in NonNullableKeys<T>]: RecursiveMaybeFields<T[K]>;
};
type AllowedRequestContext = Omit<RequestInit, 'method' | 'body'>;
export interface RequestBody {
operationName?: string;
variables: any;
query: string;
form?: FormData;
}
type QueryVariablesOption<Q extends GraphQLQuery> =
QueryVariables<Q> extends never
? {
variables?: undefined;
}
: { variables: RecursiveMaybeFields<QueryVariables<Q>> };
export type RequestOptions<Q extends GraphQLQuery> = QueryVariablesOption<Q> & {
/**
* parameter passed to `fetch` function
*/
context?: AllowedRequestContext;
/**
* Whether keep null or undefined value in variables.
*
* if `false` given, `{ a: 0, b: undefined, c: null }` will be converted to `{ a: 0 }`
*
* @default true
*/
keepNilVariables?: boolean;
};
export type QueryOptions<Q extends GraphQLQuery> = RequestOptions<Q> & {
query: Q;
};
export type MutationOptions<M extends GraphQLQuery> = RequestOptions<M> & {
mutation: M;
};
function filterEmptyValue(vars: any) {
const newVars: Record<string, any> = {};
Object.entries(vars).forEach(([key, value]) => {
if (isNil(value)) {
return;
}
if (isObject(value) && !(value instanceof File)) {
newVars[key] = filterEmptyValue(value);
return;
}
newVars[key] = value;
});
return newVars;
}
export function transformToForm(body: RequestBody) {
const form = new FormData();
const gqlBody: {
name?: string;
query: string;
variables: any;
map: any;
} = {
query: body.query,
variables: body.variables,
map: {},
};
if (body.operationName) {
gqlBody.name = body.operationName;
}
const map: Record<string, [string]> = {};
const files: File[] = [];
if (body.variables) {
let i = 0;
Object.entries(body.variables).forEach(([key, value]) => {
if (value instanceof File) {
map['0'] = [`variables.${key}`];
files[i] = value;
i++;
}
});
}
form.set('operations', JSON.stringify(gqlBody));
form.set('map', JSON.stringify(map));
for (const [i, file] of files.entries()) {
form.set(`${i}`, file);
}
return form;
}
function formatRequestBody<Q extends GraphQLQuery>({
query,
variables,
keepNilVariables,
}: QueryOptions<Q>): RequestBody | FormData {
const body: RequestBody = {
query: query.query,
variables:
keepNilVariables ?? true ? variables : filterEmptyValue(variables),
};
if (query.operationName) {
body.operationName = query.operationName;
}
if (query.containsFile) {
return transformToForm(body);
}
return body;
}
export const gqlFetcherFactory = (endpoint: string) => {
const gqlFetch = async <Query extends GraphQLQuery>(
options: QueryOptions<Query>
): Promise<QueryResponse<Query>> => {
const body = formatRequestBody(options);
const isFormData = body instanceof FormData;
const headers: Record<string, string> = {
'x-operation-name': options.query.operationName,
'x-definition-name': options.query.definitionName,
};
if (!isFormData) {
headers['content-type'] = 'application/json';
}
const ret = fetchWithTraceReport(
endpoint,
merge(options.context, {
method: 'POST',
headers,
body: isFormData ? body : JSON.stringify(body),
}),
{ event: 'GraphQLRequest' }
).then(async res => {
if (res.headers.get('content-type')?.startsWith('application/json')) {
const result = (await res.json()) as ExecutionResult;
if (res.status >= 400 || result.errors) {
if (result.errors && result.errors.length > 0) {
throw result.errors.map(
error => new GraphQLError(error.message, error)
);
} else {
throw new GraphQLError('Empty GraphQL error body');
}
} else if (result.data) {
// we have to cast here because the type of result.data is a union type
return result.data as any;
}
}
throw new GraphQLError('GraphQL query responds unexpected result');
});
return ret;
};
return gqlFetch;
};
export const fetchWithTraceReport = async (
input: RequestInfo | URL,
init?: RequestInit & { priority?: 'auto' | 'low' | 'high' }, // https://github.com/microsoft/TypeScript/issues/54472
traceOptions?: { event: string }
): Promise<Response> => {
const startTime = new Date().toISOString();
const spanId = generateRandUTF16Chars(SPAN_ID_BYTES);
const traceId = generateRandUTF16Chars(TRACE_ID_BYTES);
const traceparent = `${TRACE_VERSION}-${traceId}-${spanId}-${TRACE_FLAG}`;
init = init || {};
init.headers = init.headers || new Headers();
const requestId = nanoid();
const event = traceOptions?.event;
if (init.headers instanceof Headers) {
init.headers.append('x-request-id', requestId);
init.headers.append('traceparent', traceparent);
} else {
const headers = init.headers as Record<string, string>;
headers['x-request-id'] = requestId;
headers['traceparent'] = traceparent;
}
if (!traceReporter) {
return fetch(input, init);
}
try {
const response = await fetch(input, init);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
traceReporter!.cacheTrace(traceId, spanId, startTime, {
requestId,
...(event ? { event } : {}),
});
return response;
} catch (err) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
traceReporter!.uploadTrace(traceId, spanId, startTime, {
requestId,
...(event ? { event } : {}),
});
return await Promise.reject(err);
}
};
@@ -0,0 +1,5 @@
query checkBlobSizes($workspaceId: String!, $size: Float!) {
checkBlobSize(workspaceId: $workspaceId, size: $size) {
size
}
}
@@ -0,0 +1,3 @@
mutation deleteBlob($workspaceId: String!, $hash: String!) {
deleteBlob(workspaceId: $workspaceId, hash: $hash)
}
@@ -0,0 +1,3 @@
query listBlobs($workspaceId: String!) {
listBlobs(workspaceId: $workspaceId)
}
@@ -0,0 +1,3 @@
mutation setBlob($workspaceId: String!, $blob: Upload!) {
setBlob(workspaceId: $workspaceId, blob: $blob)
}
@@ -0,0 +1,5 @@
query blobSizes($workspaceId: String!) {
collectBlobSizes(workspaceId: $workspaceId) {
size
}
}
@@ -0,0 +1,5 @@
query allBlobSizes {
collectAllBlobSizes {
size
}
}
@@ -0,0 +1,8 @@
mutation changeEmail($token: String!) {
changeEmail(token: $token) {
id
name
avatarUrl
email
}
}
@@ -0,0 +1,8 @@
mutation changePassword($token: String!, $newPassword: String!) {
changePassword(token: $token, newPassword: $newPassword) {
id
name
avatarUrl
email
}
}
@@ -0,0 +1,7 @@
mutation createWorkspace($init: Upload!) {
createWorkspace(init: $init) {
id
public
createdAt
}
}
@@ -0,0 +1,5 @@
mutation deleteAccount {
deleteAccount {
success
}
}
@@ -0,0 +1,3 @@
mutation deleteWorkspace($id: String!) {
deleteWorkspace(id: $id)
}
@@ -0,0 +1,13 @@
query getCurrentUser {
currentUser {
id
name
email
emailVerified
avatarUrl
createdAt
token {
sessionToken
}
}
}
@@ -0,0 +1,14 @@
query getInviteInfo($inviteId: String!) {
getInviteInfo(inviteId: $inviteId) {
workspace {
id
name
avatar
}
user {
id
name
avatarUrl
}
}
}
@@ -0,0 +1,3 @@
query getIsOwner($workspaceId: String!) {
isOwner(workspaceId: $workspaceId)
}
@@ -0,0 +1,5 @@
query getMemberCountByWorkspaceId($workspaceId: String!) {
workspace(id: $workspaceId) {
memberCount
}
}
@@ -0,0 +1,14 @@
query getMembersByWorkspaceId($workspaceId: String!, $skip: Int!, $take: Int!) {
workspace(id: $workspaceId) {
members(skip: $skip, take: $take) {
id
name
email
avatarUrl
permission
inviteId
accepted
emailVerified
}
}
}
@@ -0,0 +1,5 @@
query getPublicWorkspace($id: String!) {
publicWorkspace(id: $id) {
id
}
}
@@ -0,0 +1,9 @@
query getUser($email: String!) {
user(email: $email) {
id
name
avatarUrl
email
hasPassword
}
}
@@ -0,0 +1,5 @@
query getWorkspacePublicById($id: String!) {
workspace(id: $id) {
public
}
}
@@ -0,0 +1,5 @@
query getWorkspaceSharedPages($workspaceId: String!) {
workspace(id: $workspaceId) {
sharedPages
}
}
@@ -0,0 +1,5 @@
query getWorkspace($id: String!) {
workspace(id: $id) {
id
}
}
@@ -0,0 +1,5 @@
query getWorkspaces {
workspaces {
id
}
}
@@ -0,0 +1,528 @@
/* do not manipulate this file manually. */
export interface GraphQLQuery {
id: string;
operationName: string;
definitionName: string;
query: string;
containsFile?: boolean;
}
export const checkBlobSizesQuery = {
id: 'checkBlobSizesQuery' as const,
operationName: 'checkBlobSizes',
definitionName: 'checkBlobSize',
containsFile: false,
query: `
query checkBlobSizes($workspaceId: String!, $size: Float!) {
checkBlobSize(workspaceId: $workspaceId, size: $size) {
size
}
}`,
};
export const deleteBlobMutation = {
id: 'deleteBlobMutation' as const,
operationName: 'deleteBlob',
definitionName: 'deleteBlob',
containsFile: false,
query: `
mutation deleteBlob($workspaceId: String!, $hash: String!) {
deleteBlob(workspaceId: $workspaceId, hash: $hash)
}`,
};
export const listBlobsQuery = {
id: 'listBlobsQuery' as const,
operationName: 'listBlobs',
definitionName: 'listBlobs',
containsFile: false,
query: `
query listBlobs($workspaceId: String!) {
listBlobs(workspaceId: $workspaceId)
}`,
};
export const setBlobMutation = {
id: 'setBlobMutation' as const,
operationName: 'setBlob',
definitionName: 'setBlob',
containsFile: true,
query: `
mutation setBlob($workspaceId: String!, $blob: Upload!) {
setBlob(workspaceId: $workspaceId, blob: $blob)
}`,
};
export const blobSizesQuery = {
id: 'blobSizesQuery' as const,
operationName: 'blobSizes',
definitionName: 'collectBlobSizes',
containsFile: false,
query: `
query blobSizes($workspaceId: String!) {
collectBlobSizes(workspaceId: $workspaceId) {
size
}
}`,
};
export const allBlobSizesQuery = {
id: 'allBlobSizesQuery' as const,
operationName: 'allBlobSizes',
definitionName: 'collectAllBlobSizes',
containsFile: false,
query: `
query allBlobSizes {
collectAllBlobSizes {
size
}
}`,
};
export const changeEmailMutation = {
id: 'changeEmailMutation' as const,
operationName: 'changeEmail',
definitionName: 'changeEmail',
containsFile: false,
query: `
mutation changeEmail($token: String!) {
changeEmail(token: $token) {
id
name
avatarUrl
email
}
}`,
};
export const changePasswordMutation = {
id: 'changePasswordMutation' as const,
operationName: 'changePassword',
definitionName: 'changePassword',
containsFile: false,
query: `
mutation changePassword($token: String!, $newPassword: String!) {
changePassword(token: $token, newPassword: $newPassword) {
id
name
avatarUrl
email
}
}`,
};
export const createWorkspaceMutation = {
id: 'createWorkspaceMutation' as const,
operationName: 'createWorkspace',
definitionName: 'createWorkspace',
containsFile: true,
query: `
mutation createWorkspace($init: Upload!) {
createWorkspace(init: $init) {
id
public
createdAt
}
}`,
};
export const deleteAccountMutation = {
id: 'deleteAccountMutation' as const,
operationName: 'deleteAccount',
definitionName: 'deleteAccount',
containsFile: false,
query: `
mutation deleteAccount {
deleteAccount {
success
}
}`,
};
export const deleteWorkspaceMutation = {
id: 'deleteWorkspaceMutation' as const,
operationName: 'deleteWorkspace',
definitionName: 'deleteWorkspace',
containsFile: false,
query: `
mutation deleteWorkspace($id: String!) {
deleteWorkspace(id: $id)
}`,
};
export const getCurrentUserQuery = {
id: 'getCurrentUserQuery' as const,
operationName: 'getCurrentUser',
definitionName: 'currentUser',
containsFile: false,
query: `
query getCurrentUser {
currentUser {
id
name
email
emailVerified
avatarUrl
createdAt
token {
sessionToken
}
}
}`,
};
export const getInviteInfoQuery = {
id: 'getInviteInfoQuery' as const,
operationName: 'getInviteInfo',
definitionName: 'getInviteInfo',
containsFile: false,
query: `
query getInviteInfo($inviteId: String!) {
getInviteInfo(inviteId: $inviteId) {
workspace {
id
name
avatar
}
user {
id
name
avatarUrl
}
}
}`,
};
export const getIsOwnerQuery = {
id: 'getIsOwnerQuery' as const,
operationName: 'getIsOwner',
definitionName: 'isOwner',
containsFile: false,
query: `
query getIsOwner($workspaceId: String!) {
isOwner(workspaceId: $workspaceId)
}`,
};
export const getMemberCountByWorkspaceIdQuery = {
id: 'getMemberCountByWorkspaceIdQuery' as const,
operationName: 'getMemberCountByWorkspaceId',
definitionName: 'workspace',
containsFile: false,
query: `
query getMemberCountByWorkspaceId($workspaceId: String!) {
workspace(id: $workspaceId) {
memberCount
}
}`,
};
export const getMembersByWorkspaceIdQuery = {
id: 'getMembersByWorkspaceIdQuery' as const,
operationName: 'getMembersByWorkspaceId',
definitionName: 'workspace',
containsFile: false,
query: `
query getMembersByWorkspaceId($workspaceId: String!, $skip: Int!, $take: Int!) {
workspace(id: $workspaceId) {
members(skip: $skip, take: $take) {
id
name
email
avatarUrl
permission
inviteId
accepted
emailVerified
}
}
}`,
};
export const getPublicWorkspaceQuery = {
id: 'getPublicWorkspaceQuery' as const,
operationName: 'getPublicWorkspace',
definitionName: 'publicWorkspace',
containsFile: false,
query: `
query getPublicWorkspace($id: String!) {
publicWorkspace(id: $id) {
id
}
}`,
};
export const getUserQuery = {
id: 'getUserQuery' as const,
operationName: 'getUser',
definitionName: 'user',
containsFile: false,
query: `
query getUser($email: String!) {
user(email: $email) {
id
name
avatarUrl
email
hasPassword
}
}`,
};
export const getWorkspacePublicByIdQuery = {
id: 'getWorkspacePublicByIdQuery' as const,
operationName: 'getWorkspacePublicById',
definitionName: 'workspace',
containsFile: false,
query: `
query getWorkspacePublicById($id: String!) {
workspace(id: $id) {
public
}
}`,
};
export const getWorkspaceSharedPagesQuery = {
id: 'getWorkspaceSharedPagesQuery' as const,
operationName: 'getWorkspaceSharedPages',
definitionName: 'workspace',
containsFile: false,
query: `
query getWorkspaceSharedPages($workspaceId: String!) {
workspace(id: $workspaceId) {
sharedPages
}
}`,
};
export const getWorkspaceQuery = {
id: 'getWorkspaceQuery' as const,
operationName: 'getWorkspace',
definitionName: 'workspace',
containsFile: false,
query: `
query getWorkspace($id: String!) {
workspace(id: $id) {
id
}
}`,
};
export const getWorkspacesQuery = {
id: 'getWorkspacesQuery' as const,
operationName: 'getWorkspaces',
definitionName: 'workspaces',
containsFile: false,
query: `
query getWorkspaces {
workspaces {
id
}
}`,
};
export const leaveWorkspaceMutation = {
id: 'leaveWorkspaceMutation' as const,
operationName: 'leaveWorkspace',
definitionName: 'leaveWorkspace',
containsFile: false,
query: `
mutation leaveWorkspace($workspaceId: String!, $workspaceName: String!, $sendLeaveMail: Boolean) {
leaveWorkspace(
workspaceId: $workspaceId
workspaceName: $workspaceName
sendLeaveMail: $sendLeaveMail
)
}`,
};
export const removeAvatarMutation = {
id: 'removeAvatarMutation' as const,
operationName: 'removeAvatar',
definitionName: 'removeAvatar',
containsFile: false,
query: `
mutation removeAvatar {
removeAvatar {
success
}
}`,
};
export const revokeMemberPermissionMutation = {
id: 'revokeMemberPermissionMutation' as const,
operationName: 'revokeMemberPermission',
definitionName: 'revoke',
containsFile: false,
query: `
mutation revokeMemberPermission($workspaceId: String!, $userId: String!) {
revoke(workspaceId: $workspaceId, userId: $userId)
}`,
};
export const revokePageMutation = {
id: 'revokePageMutation' as const,
operationName: 'revokePage',
definitionName: 'revokePage',
containsFile: false,
query: `
mutation revokePage($workspaceId: String!, $pageId: String!) {
revokePage(workspaceId: $workspaceId, pageId: $pageId)
}`,
};
export const sendChangeEmailMutation = {
id: 'sendChangeEmailMutation' as const,
operationName: 'sendChangeEmail',
definitionName: 'sendChangeEmail',
containsFile: false,
query: `
mutation sendChangeEmail($email: String!, $callbackUrl: String!) {
sendChangeEmail(email: $email, callbackUrl: $callbackUrl)
}`,
};
export const sendChangePasswordEmailMutation = {
id: 'sendChangePasswordEmailMutation' as const,
operationName: 'sendChangePasswordEmail',
definitionName: 'sendChangePasswordEmail',
containsFile: false,
query: `
mutation sendChangePasswordEmail($email: String!, $callbackUrl: String!) {
sendChangePasswordEmail(email: $email, callbackUrl: $callbackUrl)
}`,
};
export const sendSetPasswordEmailMutation = {
id: 'sendSetPasswordEmailMutation' as const,
operationName: 'sendSetPasswordEmail',
definitionName: 'sendSetPasswordEmail',
containsFile: false,
query: `
mutation sendSetPasswordEmail($email: String!, $callbackUrl: String!) {
sendSetPasswordEmail(email: $email, callbackUrl: $callbackUrl)
}`,
};
export const sendVerifyChangeEmailMutation = {
id: 'sendVerifyChangeEmailMutation' as const,
operationName: 'sendVerifyChangeEmail',
definitionName: 'sendVerifyChangeEmail',
containsFile: false,
query: `
mutation sendVerifyChangeEmail($token: String!, $email: String!, $callbackUrl: String!) {
sendVerifyChangeEmail(token: $token, email: $email, callbackUrl: $callbackUrl)
}`,
};
export const setWorkspacePublicByIdMutation = {
id: 'setWorkspacePublicByIdMutation' as const,
operationName: 'setWorkspacePublicById',
definitionName: 'updateWorkspace',
containsFile: false,
query: `
mutation setWorkspacePublicById($id: ID!, $public: Boolean!) {
updateWorkspace(input: {id: $id, public: $public}) {
id
}
}`,
};
export const sharePageMutation = {
id: 'sharePageMutation' as const,
operationName: 'sharePage',
definitionName: 'sharePage',
containsFile: false,
query: `
mutation sharePage($workspaceId: String!, $pageId: String!) {
sharePage(workspaceId: $workspaceId, pageId: $pageId)
}`,
};
export const signInMutation = {
id: 'signInMutation' as const,
operationName: 'signIn',
definitionName: 'signIn',
containsFile: false,
query: `
mutation signIn($email: String!, $password: String!) {
signIn(email: $email, password: $password) {
token {
token
}
}
}`,
};
export const signUpMutation = {
id: 'signUpMutation' as const,
operationName: 'signUp',
definitionName: 'signUp',
containsFile: false,
query: `
mutation signUp($name: String!, $email: String!, $password: String!) {
signUp(name: $name, email: $email, password: $password) {
token {
token
}
}
}`,
};
export const uploadAvatarMutation = {
id: 'uploadAvatarMutation' as const,
operationName: 'uploadAvatar',
definitionName: 'uploadAvatar',
containsFile: true,
query: `
mutation uploadAvatar($avatar: Upload!) {
uploadAvatar(avatar: $avatar) {
id
name
avatarUrl
email
}
}`,
};
export const inviteByEmailMutation = {
id: 'inviteByEmailMutation' as const,
operationName: 'inviteByEmail',
definitionName: 'invite',
containsFile: false,
query: `
mutation inviteByEmail($workspaceId: String!, $email: String!, $permission: Permission!, $sendInviteMail: Boolean) {
invite(
workspaceId: $workspaceId
email: $email
permission: $permission
sendInviteMail: $sendInviteMail
)
}`,
};
export const acceptInviteByInviteIdMutation = {
id: 'acceptInviteByInviteIdMutation' as const,
operationName: 'acceptInviteByInviteId',
definitionName: 'acceptInviteById',
containsFile: false,
query: `
mutation acceptInviteByInviteId($workspaceId: String!, $inviteId: String!, $sendAcceptMail: Boolean) {
acceptInviteById(
workspaceId: $workspaceId
inviteId: $inviteId
sendAcceptMail: $sendAcceptMail
)
}`,
};
export const acceptInviteByWorkspaceIdMutation = {
id: 'acceptInviteByWorkspaceIdMutation' as const,
operationName: 'acceptInviteByWorkspaceId',
definitionName: 'acceptInvite',
containsFile: false,
query: `
mutation acceptInviteByWorkspaceId($workspaceId: String!) {
acceptInvite(workspaceId: $workspaceId)
}`,
};
@@ -0,0 +1,11 @@
mutation leaveWorkspace(
$workspaceId: String!
$workspaceName: String!
$sendLeaveMail: Boolean
) {
leaveWorkspace(
workspaceId: $workspaceId
workspaceName: $workspaceName
sendLeaveMail: $sendLeaveMail
)
}
@@ -0,0 +1,5 @@
mutation removeAvatar {
removeAvatar {
success
}
}
@@ -0,0 +1,3 @@
mutation revokeMemberPermission($workspaceId: String!, $userId: String!) {
revoke(workspaceId: $workspaceId, userId: $userId)
}
@@ -0,0 +1,3 @@
mutation revokePage($workspaceId: String!, $pageId: String!) {
revokePage(workspaceId: $workspaceId, pageId: $pageId)
}
@@ -0,0 +1,3 @@
mutation sendChangeEmail($email: String!, $callbackUrl: String!) {
sendChangeEmail(email: $email, callbackUrl: $callbackUrl)
}
@@ -0,0 +1,3 @@
mutation sendChangePasswordEmail($email: String!, $callbackUrl: String!) {
sendChangePasswordEmail(email: $email, callbackUrl: $callbackUrl)
}
@@ -0,0 +1,3 @@
mutation sendSetPasswordEmail($email: String!, $callbackUrl: String!) {
sendSetPasswordEmail(email: $email, callbackUrl: $callbackUrl)
}
@@ -0,0 +1,7 @@
mutation sendVerifyChangeEmail(
$token: String!
$email: String!
$callbackUrl: String!
) {
sendVerifyChangeEmail(token: $token, email: $email, callbackUrl: $callbackUrl)
}
@@ -0,0 +1,5 @@
mutation setWorkspacePublicById($id: ID!, $public: Boolean!) {
updateWorkspace(input: { id: $id, public: $public }) {
id
}
}
@@ -0,0 +1,3 @@
mutation sharePage($workspaceId: String!, $pageId: String!) {
sharePage(workspaceId: $workspaceId, pageId: $pageId)
}
@@ -0,0 +1,7 @@
mutation signIn($email: String!, $password: String!) {
signIn(email: $email, password: $password) {
token {
token
}
}
}
@@ -0,0 +1,7 @@
mutation signUp($name: String!, $email: String!, $password: String!) {
signUp(name: $name, email: $email, password: $password) {
token {
token
}
}
}
@@ -0,0 +1,8 @@
mutation uploadAvatar($avatar: Upload!) {
uploadAvatar(avatar: $avatar) {
id
name
avatarUrl
email
}
}
@@ -0,0 +1,13 @@
mutation inviteByEmail(
$workspaceId: String!
$email: String!
$permission: Permission!
$sendInviteMail: Boolean
) {
invite(
workspaceId: $workspaceId
email: $email
permission: $permission
sendInviteMail: $sendInviteMail
)
}
@@ -0,0 +1,11 @@
mutation acceptInviteByInviteId(
$workspaceId: String!
$inviteId: String!
$sendAcceptMail: Boolean
) {
acceptInviteById(
workspaceId: $workspaceId
inviteId: $inviteId
sendAcceptMail: $sendAcceptMail
)
}
@@ -0,0 +1,3 @@
mutation acceptInviteByWorkspaceId($workspaceId: String!) {
acceptInvite(workspaceId: $workspaceId)
}
+5
View File
@@ -0,0 +1,5 @@
export * from './fetcher';
export * from './graphql';
export * from './schema';
export * from './utils';
import '@affine/env/global';
+659
View File
@@ -0,0 +1,659 @@
/* eslint-disable */
export type Maybe<T> = T | null;
export type InputMaybe<T> = T | null;
export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K];
};
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
export type MakeEmpty<
T extends { [key: string]: unknown },
K extends keyof T,
> = { [_ in K]?: never };
export type Incremental<T> =
| T
| {
[P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
};
/** All built-in and custom scalars, mapped to their actual values */
export interface Scalars {
ID: { input: string; output: string };
String: { input: string; output: string };
Boolean: { input: boolean; output: boolean };
Int: { input: number; output: number };
Float: { input: number; output: number };
/** A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. */
DateTime: { input: string; output: string };
/** The `Upload` scalar type represents a file upload. */
Upload: { input: File; output: File };
}
export enum NewFeaturesKind {
EarlyAccess = 'EarlyAccess',
}
/** User permission in workspace */
export enum Permission {
Admin = 'Admin',
Owner = 'Owner',
Read = 'Read',
Write = 'Write',
}
export interface UpdateWorkspaceInput {
id: Scalars['ID']['input'];
/** is Public workspace */
public: InputMaybe<Scalars['Boolean']['input']>;
}
export type CheckBlobSizesQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
size: Scalars['Float']['input'];
}>;
export type CheckBlobSizesQuery = {
__typename?: 'Query';
checkBlobSize: { __typename?: 'WorkspaceBlobSizes'; size: number };
};
export type DeleteBlobMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
hash: Scalars['String']['input'];
}>;
export type DeleteBlobMutation = {
__typename?: 'Mutation';
deleteBlob: boolean;
};
export type ListBlobsQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type ListBlobsQuery = { __typename?: 'Query'; listBlobs: Array<string> };
export type SetBlobMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
blob: Scalars['Upload']['input'];
}>;
export type SetBlobMutation = { __typename?: 'Mutation'; setBlob: string };
export type BlobSizesQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type BlobSizesQuery = {
__typename?: 'Query';
collectBlobSizes: { __typename?: 'WorkspaceBlobSizes'; size: number };
};
export type AllBlobSizesQueryVariables = Exact<{ [key: string]: never }>;
export type AllBlobSizesQuery = {
__typename?: 'Query';
collectAllBlobSizes: { __typename?: 'WorkspaceBlobSizes'; size: number };
};
export type ChangeEmailMutationVariables = Exact<{
token: Scalars['String']['input'];
}>;
export type ChangeEmailMutation = {
__typename?: 'Mutation';
changeEmail: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
email: string;
};
};
export type ChangePasswordMutationVariables = Exact<{
token: Scalars['String']['input'];
newPassword: Scalars['String']['input'];
}>;
export type ChangePasswordMutation = {
__typename?: 'Mutation';
changePassword: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
email: string;
};
};
export type CreateWorkspaceMutationVariables = Exact<{
init: Scalars['Upload']['input'];
}>;
export type CreateWorkspaceMutation = {
__typename?: 'Mutation';
createWorkspace: {
__typename?: 'WorkspaceType';
id: string;
public: boolean;
createdAt: string;
};
};
export type DeleteAccountMutationVariables = Exact<{ [key: string]: never }>;
export type DeleteAccountMutation = {
__typename?: 'Mutation';
deleteAccount: { __typename?: 'DeleteAccount'; success: boolean };
};
export type DeleteWorkspaceMutationVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type DeleteWorkspaceMutation = {
__typename?: 'Mutation';
deleteWorkspace: boolean;
};
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never }>;
export type GetCurrentUserQuery = {
__typename?: 'Query';
currentUser: {
__typename?: 'UserType';
id: string;
name: string;
email: string;
emailVerified: string | null;
avatarUrl: string | null;
createdAt: string | null;
token: { __typename?: 'TokenType'; sessionToken: string | null };
};
};
export type GetInviteInfoQueryVariables = Exact<{
inviteId: Scalars['String']['input'];
}>;
export type GetInviteInfoQuery = {
__typename?: 'Query';
getInviteInfo: {
__typename?: 'InvitationType';
workspace: {
__typename?: 'InvitationWorkspaceType';
id: string;
name: string;
avatar: string;
};
user: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
};
};
};
export type GetIsOwnerQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type GetIsOwnerQuery = { __typename?: 'Query'; isOwner: boolean };
export type GetMemberCountByWorkspaceIdQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type GetMemberCountByWorkspaceIdQuery = {
__typename?: 'Query';
workspace: { __typename?: 'WorkspaceType'; memberCount: number };
};
export type GetMembersByWorkspaceIdQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
skip: Scalars['Int']['input'];
take: Scalars['Int']['input'];
}>;
export type GetMembersByWorkspaceIdQuery = {
__typename?: 'Query';
workspace: {
__typename?: 'WorkspaceType';
members: Array<{
__typename?: 'InviteUserType';
id: string;
name: string | null;
email: string | null;
avatarUrl: string | null;
permission: Permission;
inviteId: string;
accepted: boolean;
emailVerified: string | null;
}>;
};
};
export type GetPublicWorkspaceQueryVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type GetPublicWorkspaceQuery = {
__typename?: 'Query';
publicWorkspace: { __typename?: 'WorkspaceType'; id: string };
};
export type GetUserQueryVariables = Exact<{
email: Scalars['String']['input'];
}>;
export type GetUserQuery = {
__typename?: 'Query';
user: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
email: string;
hasPassword: boolean | null;
} | null;
};
export type GetWorkspacePublicByIdQueryVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type GetWorkspacePublicByIdQuery = {
__typename?: 'Query';
workspace: { __typename?: 'WorkspaceType'; public: boolean };
};
export type GetWorkspaceSharedPagesQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type GetWorkspaceSharedPagesQuery = {
__typename?: 'Query';
workspace: { __typename?: 'WorkspaceType'; sharedPages: Array<string> };
};
export type GetWorkspaceQueryVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type GetWorkspaceQuery = {
__typename?: 'Query';
workspace: { __typename?: 'WorkspaceType'; id: string };
};
export type GetWorkspacesQueryVariables = Exact<{ [key: string]: never }>;
export type GetWorkspacesQuery = {
__typename?: 'Query';
workspaces: Array<{ __typename?: 'WorkspaceType'; id: string }>;
};
export type LeaveWorkspaceMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
workspaceName: Scalars['String']['input'];
sendLeaveMail: InputMaybe<Scalars['Boolean']['input']>;
}>;
export type LeaveWorkspaceMutation = {
__typename?: 'Mutation';
leaveWorkspace: boolean;
};
export type RemoveAvatarMutationVariables = Exact<{ [key: string]: never }>;
export type RemoveAvatarMutation = {
__typename?: 'Mutation';
removeAvatar: { __typename?: 'RemoveAvatar'; success: boolean };
};
export type RevokeMemberPermissionMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
userId: Scalars['String']['input'];
}>;
export type RevokeMemberPermissionMutation = {
__typename?: 'Mutation';
revoke: boolean;
};
export type RevokePageMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
pageId: Scalars['String']['input'];
}>;
export type RevokePageMutation = {
__typename?: 'Mutation';
revokePage: boolean;
};
export type SendChangeEmailMutationVariables = Exact<{
email: Scalars['String']['input'];
callbackUrl: Scalars['String']['input'];
}>;
export type SendChangeEmailMutation = {
__typename?: 'Mutation';
sendChangeEmail: boolean;
};
export type SendChangePasswordEmailMutationVariables = Exact<{
email: Scalars['String']['input'];
callbackUrl: Scalars['String']['input'];
}>;
export type SendChangePasswordEmailMutation = {
__typename?: 'Mutation';
sendChangePasswordEmail: boolean;
};
export type SendSetPasswordEmailMutationVariables = Exact<{
email: Scalars['String']['input'];
callbackUrl: Scalars['String']['input'];
}>;
export type SendSetPasswordEmailMutation = {
__typename?: 'Mutation';
sendSetPasswordEmail: boolean;
};
export type SendVerifyChangeEmailMutationVariables = Exact<{
token: Scalars['String']['input'];
email: Scalars['String']['input'];
callbackUrl: Scalars['String']['input'];
}>;
export type SendVerifyChangeEmailMutation = {
__typename?: 'Mutation';
sendVerifyChangeEmail: boolean;
};
export type SetWorkspacePublicByIdMutationVariables = Exact<{
id: Scalars['ID']['input'];
public: Scalars['Boolean']['input'];
}>;
export type SetWorkspacePublicByIdMutation = {
__typename?: 'Mutation';
updateWorkspace: { __typename?: 'WorkspaceType'; id: string };
};
export type SharePageMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
pageId: Scalars['String']['input'];
}>;
export type SharePageMutation = { __typename?: 'Mutation'; sharePage: boolean };
export type SignInMutationVariables = Exact<{
email: Scalars['String']['input'];
password: Scalars['String']['input'];
}>;
export type SignInMutation = {
__typename?: 'Mutation';
signIn: {
__typename?: 'UserType';
token: { __typename?: 'TokenType'; token: string };
};
};
export type SignUpMutationVariables = Exact<{
name: Scalars['String']['input'];
email: Scalars['String']['input'];
password: Scalars['String']['input'];
}>;
export type SignUpMutation = {
__typename?: 'Mutation';
signUp: {
__typename?: 'UserType';
token: { __typename?: 'TokenType'; token: string };
};
};
export type UploadAvatarMutationVariables = Exact<{
avatar: Scalars['Upload']['input'];
}>;
export type UploadAvatarMutation = {
__typename?: 'Mutation';
uploadAvatar: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
email: string;
};
};
export type InviteByEmailMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
email: Scalars['String']['input'];
permission: Permission;
sendInviteMail: InputMaybe<Scalars['Boolean']['input']>;
}>;
export type InviteByEmailMutation = { __typename?: 'Mutation'; invite: string };
export type AcceptInviteByInviteIdMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
inviteId: Scalars['String']['input'];
sendAcceptMail: InputMaybe<Scalars['Boolean']['input']>;
}>;
export type AcceptInviteByInviteIdMutation = {
__typename?: 'Mutation';
acceptInviteById: boolean;
};
export type AcceptInviteByWorkspaceIdMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
}>;
export type AcceptInviteByWorkspaceIdMutation = {
__typename?: 'Mutation';
acceptInvite: boolean;
};
export type Queries =
| {
name: 'checkBlobSizesQuery';
variables: CheckBlobSizesQueryVariables;
response: CheckBlobSizesQuery;
}
| {
name: 'listBlobsQuery';
variables: ListBlobsQueryVariables;
response: ListBlobsQuery;
}
| {
name: 'blobSizesQuery';
variables: BlobSizesQueryVariables;
response: BlobSizesQuery;
}
| {
name: 'allBlobSizesQuery';
variables: AllBlobSizesQueryVariables;
response: AllBlobSizesQuery;
}
| {
name: 'getCurrentUserQuery';
variables: GetCurrentUserQueryVariables;
response: GetCurrentUserQuery;
}
| {
name: 'getInviteInfoQuery';
variables: GetInviteInfoQueryVariables;
response: GetInviteInfoQuery;
}
| {
name: 'getIsOwnerQuery';
variables: GetIsOwnerQueryVariables;
response: GetIsOwnerQuery;
}
| {
name: 'getMemberCountByWorkspaceIdQuery';
variables: GetMemberCountByWorkspaceIdQueryVariables;
response: GetMemberCountByWorkspaceIdQuery;
}
| {
name: 'getMembersByWorkspaceIdQuery';
variables: GetMembersByWorkspaceIdQueryVariables;
response: GetMembersByWorkspaceIdQuery;
}
| {
name: 'getPublicWorkspaceQuery';
variables: GetPublicWorkspaceQueryVariables;
response: GetPublicWorkspaceQuery;
}
| {
name: 'getUserQuery';
variables: GetUserQueryVariables;
response: GetUserQuery;
}
| {
name: 'getWorkspacePublicByIdQuery';
variables: GetWorkspacePublicByIdQueryVariables;
response: GetWorkspacePublicByIdQuery;
}
| {
name: 'getWorkspaceSharedPagesQuery';
variables: GetWorkspaceSharedPagesQueryVariables;
response: GetWorkspaceSharedPagesQuery;
}
| {
name: 'getWorkspaceQuery';
variables: GetWorkspaceQueryVariables;
response: GetWorkspaceQuery;
}
| {
name: 'getWorkspacesQuery';
variables: GetWorkspacesQueryVariables;
response: GetWorkspacesQuery;
};
export type Mutations =
| {
name: 'deleteBlobMutation';
variables: DeleteBlobMutationVariables;
response: DeleteBlobMutation;
}
| {
name: 'setBlobMutation';
variables: SetBlobMutationVariables;
response: SetBlobMutation;
}
| {
name: 'changeEmailMutation';
variables: ChangeEmailMutationVariables;
response: ChangeEmailMutation;
}
| {
name: 'changePasswordMutation';
variables: ChangePasswordMutationVariables;
response: ChangePasswordMutation;
}
| {
name: 'createWorkspaceMutation';
variables: CreateWorkspaceMutationVariables;
response: CreateWorkspaceMutation;
}
| {
name: 'deleteAccountMutation';
variables: DeleteAccountMutationVariables;
response: DeleteAccountMutation;
}
| {
name: 'deleteWorkspaceMutation';
variables: DeleteWorkspaceMutationVariables;
response: DeleteWorkspaceMutation;
}
| {
name: 'leaveWorkspaceMutation';
variables: LeaveWorkspaceMutationVariables;
response: LeaveWorkspaceMutation;
}
| {
name: 'removeAvatarMutation';
variables: RemoveAvatarMutationVariables;
response: RemoveAvatarMutation;
}
| {
name: 'revokeMemberPermissionMutation';
variables: RevokeMemberPermissionMutationVariables;
response: RevokeMemberPermissionMutation;
}
| {
name: 'revokePageMutation';
variables: RevokePageMutationVariables;
response: RevokePageMutation;
}
| {
name: 'sendChangeEmailMutation';
variables: SendChangeEmailMutationVariables;
response: SendChangeEmailMutation;
}
| {
name: 'sendChangePasswordEmailMutation';
variables: SendChangePasswordEmailMutationVariables;
response: SendChangePasswordEmailMutation;
}
| {
name: 'sendSetPasswordEmailMutation';
variables: SendSetPasswordEmailMutationVariables;
response: SendSetPasswordEmailMutation;
}
| {
name: 'sendVerifyChangeEmailMutation';
variables: SendVerifyChangeEmailMutationVariables;
response: SendVerifyChangeEmailMutation;
}
| {
name: 'setWorkspacePublicByIdMutation';
variables: SetWorkspacePublicByIdMutationVariables;
response: SetWorkspacePublicByIdMutation;
}
| {
name: 'sharePageMutation';
variables: SharePageMutationVariables;
response: SharePageMutation;
}
| {
name: 'signInMutation';
variables: SignInMutationVariables;
response: SignInMutation;
}
| {
name: 'signUpMutation';
variables: SignUpMutationVariables;
response: SignUpMutation;
}
| {
name: 'uploadAvatarMutation';
variables: UploadAvatarMutationVariables;
response: UploadAvatarMutation;
}
| {
name: 'inviteByEmailMutation';
variables: InviteByEmailMutationVariables;
response: InviteByEmailMutation;
}
| {
name: 'acceptInviteByInviteIdMutation';
variables: AcceptInviteByInviteIdMutationVariables;
response: AcceptInviteByInviteIdMutation;
}
| {
name: 'acceptInviteByWorkspaceIdMutation';
variables: AcceptInviteByWorkspaceIdMutationVariables;
response: AcceptInviteByWorkspaceIdMutation;
};
+209
View File
@@ -0,0 +1,209 @@
export const SPAN_ID_BYTES = 8;
export const TRACE_ID_BYTES = 16;
export const TRACE_VERSION = '00';
export const TRACE_FLAG = '01';
const BytesBuffer = Array(32);
type TraceSpan = {
name: string;
spanId: string;
displayName: {
value: string;
truncatedByteCount: number;
};
startTime: string;
endTime: string;
attributes: {
attributeMap: {
requestId?: {
stringValue: {
value: string;
truncatedByteCount: number;
};
};
event?: {
stringValue: {
value: string;
truncatedByteCount: 0;
};
};
};
droppedAttributesCount: number;
};
};
/**
* inspired by open-telemetry/opentelemetry-js
*/
export function generateRandUTF16Chars(bytes: number) {
for (let i = 0; i < bytes * 2; i++) {
BytesBuffer[i] = Math.floor(Math.random() * 16) + 48;
// valid hex characters in the range 48-57 and 97-102
if (BytesBuffer[i] >= 58) {
BytesBuffer[i] += 39;
}
}
return String.fromCharCode(...BytesBuffer.slice(0, bytes * 2));
}
export class TraceReporter {
static traceReportEndpoint = process.env.TRACE_REPORT_ENDPOINT;
static shouldReportTrace = process.env.SHOULD_REPORT_TRACE;
private spansCache = new Array<TraceSpan>();
private reportIntervalId: number | undefined | NodeJS.Timeout;
private reportInterval = 60_000;
private static instance: TraceReporter;
public static getInstance(): TraceReporter {
if (!TraceReporter.instance) {
const instance = (TraceReporter.instance = new TraceReporter());
instance.initTraceReport();
}
return TraceReporter.instance;
}
public cacheTrace(
traceId: string,
spanId: string,
startTime: string,
attributes: {
requestId?: string;
event?: string;
}
) {
const span = TraceReporter.createTraceSpan(
traceId,
spanId,
startTime,
attributes
);
this.spansCache.push(span);
if (this.spansCache.length <= 1) {
this.initTraceReport();
}
}
public uploadTrace(
traceId: string,
spanId: string,
startTime: string,
attributes: {
requestId?: string;
event?: string;
}
) {
const span = TraceReporter.createTraceSpan(
traceId,
spanId,
startTime,
attributes
);
TraceReporter.reportToTraceEndpoint(JSON.stringify({ spans: [span] }));
}
public static reportToTraceEndpoint(payload: string): void {
if (!TraceReporter.traceReportEndpoint) {
console.warn('No trace report endpoint found!');
return;
}
if (typeof navigator !== 'undefined') {
navigator.sendBeacon(TraceReporter.traceReportEndpoint, payload);
} else {
fetch(TraceReporter.traceReportEndpoint, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: payload,
}).catch(console.warn);
}
}
public static createTraceSpan(
traceId: string,
spanId: string,
startTime: string,
attributes: {
requestId?: string;
event?: string;
}
): TraceSpan {
const requestId = attributes.requestId;
const event = attributes.event;
return {
name: `projects/{GCP_PROJECT_ID}/traces/${traceId}/spans/${spanId}`,
spanId,
displayName: {
value: 'AFFiNE_REQUEST',
truncatedByteCount: 0,
},
startTime,
endTime: new Date().toISOString(),
attributes: {
attributeMap: {
...(!requestId
? {}
: {
requestId: {
stringValue: {
value: requestId,
truncatedByteCount: 0,
},
},
}),
...(!event
? {}
: {
event: {
stringValue: {
value: event,
truncatedByteCount: 0,
},
},
}),
},
droppedAttributesCount: 0,
},
};
}
private initTraceReport = () => {
if (!this.reportIntervalId && TraceReporter.shouldReportTrace) {
if (typeof window !== 'undefined') {
this.reportIntervalId = window.setInterval(
this.reportHandler,
this.reportInterval
);
} else {
this.reportIntervalId = setInterval(
this.reportHandler,
this.reportInterval
);
}
}
};
private reportHandler = () => {
if (this.spansCache.length <= 0) {
clearInterval(this.reportIntervalId);
this.reportIntervalId = undefined;
return;
}
TraceReporter.reportToTraceEndpoint(
JSON.stringify({ spans: [...this.spansCache] })
);
this.spansCache = [];
};
}
export const traceReporter = process.env.SHOULD_REPORT_TRACE
? TraceReporter.getInstance()
: null;