mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(infra): directory structure (#4615)
This commit is contained in:
3
packages/frontend/graphql/README.md
Normal file
3
packages/frontend/graphql/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `GraphQL` client
|
||||
|
||||
Auto generated `GraphQL` client for affine.pro
|
||||
32
packages/frontend/graphql/codegen.yml
Normal file
32
packages/frontend/graphql/codegen.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
hooks:
|
||||
afterOneFileWrite:
|
||||
- prettier --write
|
||||
config:
|
||||
strict: true
|
||||
maybeValue: T | null
|
||||
declarationKind: interface
|
||||
avoidOptionals: true
|
||||
preResolveTypes: true
|
||||
onlyOperationTypes: true
|
||||
namingConvention:
|
||||
enumValues: keep
|
||||
scalars:
|
||||
DateTime: string
|
||||
Date: string
|
||||
Decimal: number
|
||||
UUID: string
|
||||
ID: string
|
||||
JSON: any
|
||||
Upload: File
|
||||
overwrite: true
|
||||
schema: ../../backend/server/src/schema.gql
|
||||
documents: ./src/**/*.gql
|
||||
generates:
|
||||
./src/schema.ts:
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-operations
|
||||
- add:
|
||||
content: '/* eslint-disable */'
|
||||
- ./export-gql-plugin.cjs:
|
||||
output: ./src/graphql/index.ts
|
||||
224
packages/frontend/graphql/export-gql-plugin.cjs
Normal file
224
packages/frontend/graphql/export-gql-plugin.cjs
Normal file
@@ -0,0 +1,224 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { Kind, print } = require('graphql');
|
||||
const { upperFirst, lowerFirst } = require('lodash');
|
||||
|
||||
/**
|
||||
* return exported name used in runtime.
|
||||
*
|
||||
* @param {import('graphql').ExecutableDefinitionNode} def
|
||||
* @returns {string}
|
||||
*/
|
||||
function getExportedName(def) {
|
||||
const name = lowerFirst(def.name?.value);
|
||||
const suffix =
|
||||
def.kind === Kind.OPERATION_DEFINITION
|
||||
? upperFirst(def.operation)
|
||||
: 'Fragment';
|
||||
return name.endsWith(suffix) ? name : name + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {import('@graphql-codegen/plugin-helpers').CodegenPlugin}
|
||||
*/
|
||||
module.exports = {
|
||||
plugin: (_schema, documents, { output }) => {
|
||||
const nameLocationMap = new Map();
|
||||
const locationSourceMap = new Map(
|
||||
documents
|
||||
.filter(source => !!source.location)
|
||||
.map(source => [source.location, source])
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
const defs = [];
|
||||
const queries = [];
|
||||
const mutations = [];
|
||||
|
||||
for (const [location, source] of locationSourceMap) {
|
||||
if (
|
||||
!source ||
|
||||
!source.document ||
|
||||
!location ||
|
||||
source.document.kind !== Kind.DOCUMENT ||
|
||||
!source.document.definitions ||
|
||||
!source.document.definitions.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = source.document;
|
||||
|
||||
if (doc.definitions.length > 1) {
|
||||
throw new Error('Only support one definition per file.');
|
||||
}
|
||||
const definition = doc.definitions[0];
|
||||
if (!definition) {
|
||||
throw new Error(`Found empty file ${location}.`);
|
||||
}
|
||||
|
||||
if (
|
||||
!definition.selectionSet ||
|
||||
!definition.selectionSet.selections ||
|
||||
definition.selectionSet.selections.length === 0
|
||||
) {
|
||||
throw new Error(`Found empty fields selection in file ${location}`);
|
||||
}
|
||||
|
||||
if (
|
||||
definition.kind === Kind.OPERATION_DEFINITION ||
|
||||
definition.kind === Kind.FRAGMENT_DEFINITION
|
||||
) {
|
||||
if (!definition.name) {
|
||||
throw new Error(`Anonymous definition found in ${location}`);
|
||||
}
|
||||
|
||||
const exportedName = getExportedName(definition);
|
||||
|
||||
// duplication checking
|
||||
if (nameLocationMap.has(exportedName)) {
|
||||
throw new Error(
|
||||
`name ${exportedName} export from ${location} are duplicated.`
|
||||
);
|
||||
} else {
|
||||
/**
|
||||
* @type {import('graphql').DefinitionNode[]}
|
||||
*/
|
||||
let importedDefinitions = [];
|
||||
if (source.location) {
|
||||
fs.readFileSync(source.location, 'utf8')
|
||||
.split(/\r\n|\r|\n/)
|
||||
.forEach(line => {
|
||||
if (line[0] === '#') {
|
||||
const [importKeyword, importPath] = line
|
||||
.split(' ')
|
||||
.filter(Boolean);
|
||||
if (importKeyword === '#import') {
|
||||
const realImportPath = path.posix.join(
|
||||
location,
|
||||
'..',
|
||||
importPath.replace(/["']/g, '')
|
||||
);
|
||||
const imports =
|
||||
locationSourceMap.get(realImportPath)?.document
|
||||
.definitions;
|
||||
if (imports) {
|
||||
importedDefinitions = [
|
||||
...importedDefinitions,
|
||||
...imports,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const importing = importedDefinitions
|
||||
.map(def => `\${${getExportedName(def)}}`)
|
||||
.join('\n');
|
||||
|
||||
// is query or mutation
|
||||
if (definition.kind === Kind.OPERATION_DEFINITION) {
|
||||
// add for runtime usage
|
||||
doc.operationName = definition.name.value;
|
||||
doc.defName = definition.selectionSet.selections
|
||||
.filter(field => field.kind === Kind.FIELD)
|
||||
.map(field => field.name.value)
|
||||
.join(',');
|
||||
nameLocationMap.set(exportedName, location);
|
||||
const containsFile = doc.definitions.some(def => {
|
||||
const { variableDefinitions } = def;
|
||||
if (variableDefinitions) {
|
||||
return variableDefinitions.some(variableDefinition => {
|
||||
if (
|
||||
variableDefinition?.type?.type?.name?.value === 'Upload'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
defs.push(`export const ${exportedName} = {
|
||||
id: '${exportedName}' as const,
|
||||
operationName: '${doc.operationName}',
|
||||
definitionName: '${doc.defName}',
|
||||
containsFile: ${containsFile},
|
||||
query: \`
|
||||
${print(doc)}${importing || ''}\`,
|
||||
};
|
||||
`);
|
||||
if (definition.operation === 'query') {
|
||||
queries.push(exportedName);
|
||||
} else if (definition.operation === 'mutation') {
|
||||
mutations.push(exportedName);
|
||||
}
|
||||
} else {
|
||||
defs.unshift(`export const ${exportedName} = \`
|
||||
${print(doc)}${importing || ''}\``);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
output,
|
||||
[
|
||||
'/* do not manipulate this file manually. */',
|
||||
`export interface GraphQLQuery {
|
||||
id: string;
|
||||
operationName: string;
|
||||
definitionName: string;
|
||||
query: string;
|
||||
containsFile?: boolean;
|
||||
}
|
||||
`,
|
||||
...defs,
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
const queriesUnion = queries
|
||||
.map(query => {
|
||||
const queryName = upperFirst(query);
|
||||
return `{
|
||||
name: '${query}',
|
||||
variables: ${queryName}Variables,
|
||||
response: ${queryName}
|
||||
}
|
||||
`;
|
||||
})
|
||||
.join('|');
|
||||
|
||||
const mutationsUnion = mutations
|
||||
.map(query => {
|
||||
const queryName = upperFirst(query);
|
||||
return `{
|
||||
name: '${query}',
|
||||
variables: ${queryName}Variables,
|
||||
response: ${queryName}
|
||||
}
|
||||
`;
|
||||
})
|
||||
.join('|');
|
||||
const queryTypes = queriesUnion
|
||||
? `export type Queries = ${queriesUnion}`
|
||||
: '';
|
||||
const mutationsTypes = mutationsUnion
|
||||
? `export type Mutations = ${mutationsUnion}`
|
||||
: '';
|
||||
return `
|
||||
${queryTypes}
|
||||
${mutationsTypes}
|
||||
`;
|
||||
},
|
||||
validate: (_schema, _documents, { output }) => {
|
||||
if (!output) {
|
||||
throw new Error('Export plugin must be used with a output file given');
|
||||
}
|
||||
},
|
||||
};
|
||||
33
packages/frontend/graphql/package.json
Normal file
33
packages/frontend/graphql/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@affine/graphql",
|
||||
"version": "0.10.0-canary.1",
|
||||
"description": "Autogenerated GraphQL client for affine.pro",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^5.0.0",
|
||||
"@graphql-codegen/cli": "5.0.0",
|
||||
"@graphql-codegen/typescript": "^4.0.1",
|
||||
"@graphql-codegen/typescript-operations": "^4.0.1",
|
||||
"@types/lodash-es": "^4.17.9",
|
||||
"lodash-es": "^4.17.21",
|
||||
"prettier": "^3.0.3",
|
||||
"vitest": "0.34.6"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "gql-gen"
|
||||
},
|
||||
"dependencies": {
|
||||
"@affine/env": "workspace:*",
|
||||
"graphql": "^16.8.1",
|
||||
"nanoid": "^5.0.1"
|
||||
},
|
||||
"installConfig": {
|
||||
"hoistingLimits": "workspaces"
|
||||
}
|
||||
}
|
||||
159
packages/frontend/graphql/src/__tests__/fetcher.spec.ts
Normal file
159
packages/frontend/graphql/src/__tests__/fetcher.spec.ts
Normal file
@@ -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
packages/frontend/graphql/src/fetcher.ts
Normal file
260
packages/frontend/graphql/src/fetcher.ts
Normal 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
|
||||
}
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/blob-delete.gql
Normal file
3
packages/frontend/graphql/src/graphql/blob-delete.gql
Normal file
@@ -0,0 +1,3 @@
|
||||
mutation deleteBlob($workspaceId: String!, $hash: String!) {
|
||||
deleteBlob(workspaceId: $workspaceId, hash: $hash)
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/blob-list.gql
Normal file
3
packages/frontend/graphql/src/graphql/blob-list.gql
Normal file
@@ -0,0 +1,3 @@
|
||||
query listBlobs($workspaceId: String!) {
|
||||
listBlobs(workspaceId: $workspaceId)
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/blob-set.gql
Normal file
3
packages/frontend/graphql/src/graphql/blob-set.gql
Normal file
@@ -0,0 +1,3 @@
|
||||
mutation setBlob($workspaceId: String!, $blob: Upload!) {
|
||||
setBlob(workspaceId: $workspaceId, blob: $blob)
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/blob-size.gql
Normal file
5
packages/frontend/graphql/src/graphql/blob-size.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
query blobSizes($workspaceId: String!) {
|
||||
collectBlobSizes(workspaceId: $workspaceId) {
|
||||
size
|
||||
}
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/blobs-size.gql
Normal file
5
packages/frontend/graphql/src/graphql/blobs-size.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
query allBlobSizes {
|
||||
collectAllBlobSizes {
|
||||
size
|
||||
}
|
||||
}
|
||||
8
packages/frontend/graphql/src/graphql/change-email.gql
Normal file
8
packages/frontend/graphql/src/graphql/change-email.gql
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/delete-account.gql
Normal file
5
packages/frontend/graphql/src/graphql/delete-account.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
mutation deleteAccount {
|
||||
deleteAccount {
|
||||
success
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation deleteWorkspace($id: String!) {
|
||||
deleteWorkspace(id: $id)
|
||||
}
|
||||
13
packages/frontend/graphql/src/graphql/get-current-user.gql
Normal file
13
packages/frontend/graphql/src/graphql/get-current-user.gql
Normal file
@@ -0,0 +1,13 @@
|
||||
query getCurrentUser {
|
||||
currentUser {
|
||||
id
|
||||
name
|
||||
email
|
||||
emailVerified
|
||||
avatarUrl
|
||||
createdAt
|
||||
token {
|
||||
sessionToken
|
||||
}
|
||||
}
|
||||
}
|
||||
14
packages/frontend/graphql/src/graphql/get-invite-info.gql
Normal file
14
packages/frontend/graphql/src/graphql/get-invite-info.gql
Normal file
@@ -0,0 +1,14 @@
|
||||
query getInviteInfo($inviteId: String!) {
|
||||
getInviteInfo(inviteId: $inviteId) {
|
||||
workspace {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
}
|
||||
user {
|
||||
id
|
||||
name
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/get-is-owner.gql
Normal file
3
packages/frontend/graphql/src/graphql/get-is-owner.gql
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
9
packages/frontend/graphql/src/graphql/get-user.gql
Normal file
9
packages/frontend/graphql/src/graphql/get-user.gql
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/get-workspace.gql
Normal file
5
packages/frontend/graphql/src/graphql/get-workspace.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
query getWorkspace($id: String!) {
|
||||
workspace(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/get-workspaces.gql
Normal file
5
packages/frontend/graphql/src/graphql/get-workspaces.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
query getWorkspaces {
|
||||
workspaces {
|
||||
id
|
||||
}
|
||||
}
|
||||
528
packages/frontend/graphql/src/graphql/index.ts
Normal file
528
packages/frontend/graphql/src/graphql/index.ts
Normal file
@@ -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)
|
||||
}`,
|
||||
};
|
||||
11
packages/frontend/graphql/src/graphql/leave-workspace.gql
Normal file
11
packages/frontend/graphql/src/graphql/leave-workspace.gql
Normal file
@@ -0,0 +1,11 @@
|
||||
mutation leaveWorkspace(
|
||||
$workspaceId: String!
|
||||
$workspaceName: String!
|
||||
$sendLeaveMail: Boolean
|
||||
) {
|
||||
leaveWorkspace(
|
||||
workspaceId: $workspaceId
|
||||
workspaceName: $workspaceName
|
||||
sendLeaveMail: $sendLeaveMail
|
||||
)
|
||||
}
|
||||
5
packages/frontend/graphql/src/graphql/remove-avatar.gql
Normal file
5
packages/frontend/graphql/src/graphql/remove-avatar.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
mutation removeAvatar {
|
||||
removeAvatar {
|
||||
success
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation revokeMemberPermission($workspaceId: String!, $userId: String!) {
|
||||
revoke(workspaceId: $workspaceId, userId: $userId)
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/revoke-page.gql
Normal file
3
packages/frontend/graphql/src/graphql/revoke-page.gql
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
3
packages/frontend/graphql/src/graphql/share-page.gql
Normal file
3
packages/frontend/graphql/src/graphql/share-page.gql
Normal file
@@ -0,0 +1,3 @@
|
||||
mutation sharePage($workspaceId: String!, $pageId: String!) {
|
||||
sharePage(workspaceId: $workspaceId, pageId: $pageId)
|
||||
}
|
||||
7
packages/frontend/graphql/src/graphql/sign-in.gql
Normal file
7
packages/frontend/graphql/src/graphql/sign-in.gql
Normal file
@@ -0,0 +1,7 @@
|
||||
mutation signIn($email: String!, $password: String!) {
|
||||
signIn(email: $email, password: $password) {
|
||||
token {
|
||||
token
|
||||
}
|
||||
}
|
||||
}
|
||||
7
packages/frontend/graphql/src/graphql/sign-up.gql
Normal file
7
packages/frontend/graphql/src/graphql/sign-up.gql
Normal file
@@ -0,0 +1,7 @@
|
||||
mutation signUp($name: String!, $email: String!, $password: String!) {
|
||||
signUp(name: $name, email: $email, password: $password) {
|
||||
token {
|
||||
token
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/frontend/graphql/src/graphql/upload-avatar.gql
Normal file
8
packages/frontend/graphql/src/graphql/upload-avatar.gql
Normal file
@@ -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
packages/frontend/graphql/src/index.ts
Normal file
5
packages/frontend/graphql/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './fetcher';
|
||||
export * from './graphql';
|
||||
export * from './schema';
|
||||
export * from './utils';
|
||||
import '@affine/env/global';
|
||||
659
packages/frontend/graphql/src/schema.ts
Normal file
659
packages/frontend/graphql/src/schema.ts
Normal 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
packages/frontend/graphql/src/utils.ts
Normal file
209
packages/frontend/graphql/src/utils.ts
Normal 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;
|
||||
14
packages/frontend/graphql/tsconfig.json
Normal file
14
packages/frontend/graphql/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": ["./src"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": false,
|
||||
"outDir": "lib"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common/env"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user