feat(server): auth server (#2773)

This commit is contained in:
LongYinan
2023-06-21 14:08:32 +08:00
committed by GitHub
parent 2698e7fd0d
commit 9b3fa43b81
36 changed files with 4089 additions and 2013 deletions

View File

@@ -17,6 +17,7 @@ config:
UUID: string
ID: string
JSON: any
Upload: File
overwrite: true
schema: ../../apps/server/src/schema.gql
documents: ./src/**/*.gql

View File

@@ -129,10 +129,26 @@ module.exports = {
.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 || ''}\`,
}
@@ -159,6 +175,7 @@ ${print(doc)}${importing || ''}\``);
operationName: string
definitionName: string
query: string
containsFile?: boolean
}
`,
...defs,

View File

@@ -18,6 +18,9 @@
"lodash-es": "^4.17.21",
"prettier": "^2.8.8"
},
"scripts": {
"postinstall": "gql-gen"
},
"dependencies": {
"graphql": "^16.6.0"
}

View File

@@ -102,7 +102,8 @@ describe('GraphQL fetcher', () => {
)
);
await expect(gql({ query, variables: {} })).rejects.toMatchInlineSnapshot(`
await expect(gql({ query, variables: void 0 })).rejects
.toMatchInlineSnapshot(`
[
[GraphQLError: error],
]

View File

@@ -7,10 +7,12 @@ import type { Mutations, Queries } from './schema';
export type NotArray<T> = T extends Array<unknown> ? never : T;
type _QueryVariables<Q extends GraphQLQuery> = Extract<
Queries | Mutations,
{ name: Q['id'] }
>['variables'];
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
@@ -65,13 +67,6 @@ export type RequestOptions<Q extends GraphQLQuery> = QueryVariablesOption<Q> & {
* parameter passed to `fetch` function
*/
context?: AllowedRequestContext;
/**
* files need to be uploaded
*
* When provided, the request body will be turned to multiparts to satisfy
* file uploading scene.
*/
files?: File[];
/**
* Whether keep null or undefined value in variables.
*
@@ -105,27 +100,43 @@ function filterEmptyValue(vars: any) {
return newVars;
}
export function appendFormData(body: RequestBody, files: File[]) {
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) {
form.append('operationName', body.operationName);
gqlBody.name = body.operationName;
}
form.append('query', body.query);
form.append('variables', JSON.stringify(body.variables));
files.forEach(file => {
form.append(file.name, file);
});
body.form = form;
if (body.variables) {
let i = 0;
Object.entries(body.variables).forEach(([key, value]) => {
if (value instanceof File) {
gqlBody.map['0'] = [`variables.${key}`];
form.append(`${i}`, value);
i++;
}
});
}
form.append('operations', JSON.stringify(gqlBody));
return form;
}
function formatRequestBody<Q extends GraphQLQuery>({
query,
variables,
keepNilVariables,
files,
}: QueryOptions<Q>): RequestBody {
}: QueryOptions<Q>): RequestBody | FormData {
const body: RequestBody = {
query: query.query,
variables:
@@ -136,10 +147,9 @@ function formatRequestBody<Q extends GraphQLQuery>({
body.operationName = query.operationName;
}
if (files?.length) {
appendFormData(body, files);
if (query.containsFile) {
return transformToForm(body);
}
return body;
}
@@ -157,7 +167,7 @@ export const gqlFetcherFactory = (endpoint: string) => {
'x-operation-name': options.query.operationName,
'x-definition-name': options.query.definitionName,
},
body: body.form ?? JSON.stringify(body),
body: body instanceof FormData ? body : JSON.stringify(body),
})
).then(async res => {
if (res.headers.get('content-type') === 'application/json') {

View File

@@ -2,6 +2,6 @@ mutation createWorkspace {
createWorkspace {
id
public
created_at
createdAt
}
}

View File

@@ -4,18 +4,36 @@ export interface GraphQLQuery {
operationName: string;
definitionName: string;
query: string;
containsFile?: boolean;
}
export const createWorkspaceMutation = {
id: 'createWorkspaceMutation' as const,
operationName: 'createWorkspace',
definitionName: 'createWorkspace',
containsFile: false,
query: `
mutation createWorkspace {
createWorkspace {
id
public
created_at
createdAt
}
}`,
};
export const uploadAvatarMutation = {
id: 'uploadAvatarMutation' as const,
operationName: 'uploadAvatar',
definitionName: 'uploadAvatar',
containsFile: true,
query: `
mutation uploadAvatar($id: String!, $avatar: Upload!) {
uploadAvatar(id: $id, avatar: $avatar) {
id
name
avatarUrl
email
}
}`,
};
@@ -24,13 +42,13 @@ export const workspaceByIdQuery = {
id: 'workspaceByIdQuery' as const,
operationName: 'workspaceById',
definitionName: 'workspace',
containsFile: false,
query: `
query workspaceById($id: String!) {
workspace(id: $id) {
id
type
public
created_at
createdAt
}
}`,
};

View File

@@ -0,0 +1,8 @@
mutation uploadAvatar($id: String!, $avatar: Upload!) {
uploadAvatar(id: $id, avatar: $avatar) {
id
name
avatarUrl
email
}
}

View File

@@ -1,8 +1,7 @@
query workspaceById($id: String!) {
workspace(id: $id) {
id
type
public
created_at
createdAt
}
}

View File

@@ -10,23 +10,40 @@ export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
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: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
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: string;
DateTime: { input: string; output: string };
/** The `Upload` scalar type represents a file upload. */
Upload: { input: File; output: File };
}
/** Workspace type */
export enum WorkspaceType {
/** Normal workspace */
Normal = 'Normal',
/** Private workspace */
Private = 'Private',
/** 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 CreateWorkspaceMutationVariables = Exact<{ [key: string]: never }>;
@@ -34,25 +51,40 @@ export type CreateWorkspaceMutationVariables = Exact<{ [key: string]: never }>;
export type CreateWorkspaceMutation = {
__typename?: 'Mutation';
createWorkspace: {
__typename?: 'Workspace';
__typename?: 'WorkspaceType';
id: string;
public: boolean;
created_at: string;
createdAt: string;
};
};
export type UploadAvatarMutationVariables = Exact<{
id: Scalars['String']['input'];
avatar: Scalars['Upload']['input'];
}>;
export type UploadAvatarMutation = {
__typename?: 'Mutation';
uploadAvatar: {
__typename?: 'UserType';
id: string;
name: string;
avatarUrl: string | null;
email: string;
};
};
export type WorkspaceByIdQueryVariables = Exact<{
id: Scalars['String'];
id: Scalars['String']['input'];
}>;
export type WorkspaceByIdQuery = {
__typename?: 'Query';
workspace: {
__typename?: 'Workspace';
__typename?: 'WorkspaceType';
id: string;
type: WorkspaceType;
public: boolean;
created_at: string;
createdAt: string;
};
};
@@ -62,8 +94,14 @@ export type Queries = {
response: WorkspaceByIdQuery;
};
export type Mutations = {
name: 'createWorkspaceMutation';
variables: CreateWorkspaceMutationVariables;
response: CreateWorkspaceMutation;
};
export type Mutations =
| {
name: 'createWorkspaceMutation';
variables: CreateWorkspaceMutationVariables;
response: CreateWorkspaceMutation;
}
| {
name: 'uploadAvatarMutation';
variables: UploadAvatarMutationVariables;
response: UploadAvatarMutation;
};