mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-14 08:36:22 +08:00
26db1d436d
standardize the error raising in both GraphQL Resolvers and Controllers.
Now, All user aware errors should be throwed with `HttpException`'s variants, for example `NotFoundException`.
> Directly throwing `GraphQLError` are forbidden.
The GraphQL errorFormatter will handle it automatically and set `code`, `status` in error extensions.
At the same time, the frontend `GraphQLError` should be imported from `@affine/graphql`, which introduce a better error extensions type.
----
controller example:
```js
@Get('/docs/${id}')
doc() {
// ...
// imported from '@nestjs/common'
throw new NotFoundException('Doc is not found.');
// ...
}
```
the above will response as:
```
status: 404 Not Found
{
"message": "Doc is not found.",
"statusCode": 404,
"error": "Not Found"
}
```
resolver example:
```js
@Mutation()
invite() {
// ...
throw new PayloadTooLargeException('Workspace seats is full.')
// ...
}
```
the above will response as:
```
status: 200 Ok
{
"data": null,
"errors": [
{
"message": "Workspace seats is full.",
"extensions": {
"code": 404,
"status": "Not Found"
}
}
]
}
```
for frontend GraphQLError user-friend, a helper function introduced:
```js
import { findGraphQLError } from '@affine/graphql'
fetch(query)
.catch(errOrArr => {
const e = findGraphQLError(errOrArr, e => e.extensions.code === 404)
if (e) {
// handle
}
})
```
78 lines
1.7 KiB
TypeScript
78 lines
1.7 KiB
TypeScript
import {
|
|
deleteBlobMutation,
|
|
fetchWithTraceReport,
|
|
findGraphQLError,
|
|
getBaseUrl,
|
|
listBlobsQuery,
|
|
setBlobMutation,
|
|
} from '@affine/graphql';
|
|
import { fetcher } from '@affine/graphql';
|
|
import { type BlobStorage, BlobStorageOverCapacity } from '@toeverything/infra';
|
|
|
|
import { bufferToBlob } from '../utils/buffer-to-blob';
|
|
|
|
export class AffineCloudBlobStorage implements BlobStorage {
|
|
constructor(private readonly workspaceId: string) {}
|
|
|
|
name = 'affine-cloud';
|
|
readonly = false;
|
|
|
|
async get(key: string) {
|
|
const suffix = key.startsWith('/')
|
|
? key
|
|
: `/api/workspaces/${this.workspaceId}/blobs/${key}`;
|
|
|
|
return fetchWithTraceReport(getBaseUrl() + suffix).then(async res => {
|
|
if (!res.ok) {
|
|
// status not in the range 200-299
|
|
return null;
|
|
}
|
|
return bufferToBlob(await res.arrayBuffer());
|
|
});
|
|
}
|
|
|
|
async set(key: string, value: Blob) {
|
|
// set blob will check blob size & quota
|
|
return await fetcher({
|
|
query: setBlobMutation,
|
|
variables: {
|
|
workspaceId: this.workspaceId,
|
|
blob: new File([value], key),
|
|
},
|
|
})
|
|
.then(res => res.setBlob)
|
|
.catch(err => {
|
|
const uploadError = findGraphQLError(
|
|
err,
|
|
e => e.extensions.code === 413
|
|
);
|
|
|
|
if (uploadError) {
|
|
throw new BlobStorageOverCapacity(uploadError);
|
|
}
|
|
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
async delete(key: string) {
|
|
await fetcher({
|
|
query: deleteBlobMutation,
|
|
variables: {
|
|
workspaceId: key,
|
|
hash: key,
|
|
},
|
|
});
|
|
}
|
|
|
|
async list() {
|
|
const result = await fetcher({
|
|
query: listBlobsQuery,
|
|
variables: {
|
|
workspaceId: this.workspaceId,
|
|
},
|
|
});
|
|
return result.listBlobs;
|
|
}
|
|
}
|