feat: add public workspace page api (#1791)

This commit is contained in:
Himself65
2023-04-03 06:15:39 -05:00
committed by GitHub
parent 196b9f2dbb
commit 5dbbabae57
3 changed files with 133 additions and 16 deletions

View File

@@ -7,12 +7,16 @@ import { readFile } from 'node:fs/promises';
import { MessageCode } from '@affine/env/constant';
import { createStatusApis } from '@affine/workspace/affine/api/status';
import type { KeckProvider } from '@affine/workspace/affine/keck';
import user1 from '@affine-test/fixtures/built-in-user1.json';
import user2 from '@affine-test/fixtures/built-in-user2.json';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import { assertExists } from '@blocksuite/global/utils';
import type { Page } from '@blocksuite/store';
import { Workspace } from '@blocksuite/store';
import { faker } from '@faker-js/faker';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { applyUpdate } from 'yjs';
import {
createUserApis,
@@ -33,6 +37,21 @@ let userApis: ReturnType<typeof createUserApis>;
let affineAuth: ReturnType<typeof createAffineAuth>;
let statusApis: ReturnType<typeof createStatusApis>;
function initPage(page: Page) {
// Add page block and surface block at root level
const pageBlockId = page.addBlock('affine:page', {
title: new page.Text(''),
});
page.addBlock('affine:surface', {}, null);
const frameId = page.addBlock('affine:frame', {}, pageBlockId);
page.addBlock('affine:paragraph', {}, frameId);
page.resetHistory();
return {
pageBlockId,
frameId,
} as const;
}
const mockUser = {
name: faker.name.fullName(),
email: faker.internet.email(),
@@ -47,10 +66,10 @@ beforeEach(() => {
});
beforeEach(() => {
affineAuth = createAffineAuth('http://localhost:3000/');
userApis = createUserApis('http://localhost:3000/');
workspaceApis = createWorkspaceApis('http://localhost:3000/');
statusApis = createStatusApis('http://localhost:3000/');
affineAuth = createAffineAuth('http://127.0.0.1:3000/');
userApis = createUserApis('http://127.0.0.1:3000/');
workspaceApis = createWorkspaceApis('http://127.0.0.1:3000/');
statusApis = createStatusApis('http://127.0.0.1:3000/');
});
beforeEach(async () => {
@@ -58,7 +77,7 @@ beforeEach(async () => {
});
beforeEach(async () => {
const data = await fetch('http://localhost:3000/api/user/token', {
const data = await fetch('http://127.0.0.1:3000/api/user/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -80,21 +99,37 @@ declare global {
}
}
const wsUrl = `ws://127.0.0.1:3000/api/sync/`;
async function createWorkspace(
workspaceApi: typeof workspaceApis
workspaceApi: typeof workspaceApis,
callback?: (workspace: Workspace) => void
): Promise<string> {
const workspace = new Workspace({
id: faker.datatype.uuid(),
});
})
.register(AffineSchemas)
.register(__unstableSchemas);
if (callback) {
callback(workspace);
}
const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc);
const data = await workspaceApi.createWorkspace(new Blob([binary]));
const data = await workspaceApi.createWorkspace(binary);
function waitForConnected(provider: KeckProvider) {
return new Promise<void>(resolve => {
provider.once('status', ({ status }: any) => {
expect(status).toBe('connected');
resolve();
});
});
}
createWorkspaceResponseSchema.parse(data);
return data.id;
}
describe('api', () => {
test('built-in mock user', async () => {
const data = await fetch('http://localhost:3000/api/user/token', {
const data = await fetch('http://127.0.0.1:3000/api/user/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -106,7 +141,7 @@ describe('api', () => {
}),
}).then(r => r.json());
loginResponseSchema.parse(data);
const data2 = await fetch('http://localhost:3000/api/user/token', {
const data2 = await fetch('http://127.0.0.1:3000/api/user/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -121,7 +156,7 @@ describe('api', () => {
});
test('failed', async () => {
workspaceApis = createWorkspaceApis('http://localhost:10086/404/');
workspaceApis = createWorkspaceApis('http://127.0.0.1:10086/404/');
const listener = vi.fn(
(
e: CustomEvent<{
@@ -205,7 +240,7 @@ describe('api', () => {
const path = require.resolve('@affine-test/fixtures/smile.png');
const imageBuffer = await readFile(path);
const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc);
const data = await workspaceApis.createWorkspace(new Blob([binary]));
const data = await workspaceApis.createWorkspace(binary);
createWorkspaceResponseSchema.parse(data);
const workspaceId = data.id;
const blobId = await workspaceApis.uploadBlob(
@@ -244,6 +279,76 @@ describe('api', () => {
}
);
test('workspace page binary', async () => {
const id = await createWorkspace(workspaceApis, workspace => {
{
const page = workspace.createPage('page0');
const { frameId } = initPage(page);
page.addBlock(
'affine:paragraph',
{
text: new page.Text('This is page0'),
},
frameId
);
}
{
const page = workspace.createPage('page1');
const { frameId } = initPage(page);
page.addBlock(
'affine:paragraph',
{
text: new page.Text('This is page1'),
},
frameId
);
}
});
await workspaceApis.updateWorkspace({
id,
public: true,
});
{
const binary = await workspaceApis.downloadWorkspace(id, false);
const workspace = new Workspace({
id: faker.datatype.uuid(),
})
.register(AffineSchemas)
.register(__unstableSchemas);
applyUpdate(workspace.doc, new Uint8Array(binary));
expect(workspace.getPage('page0')).not.toBeUndefined();
expect(workspace.getPage('page1')).not.toBeUndefined();
}
{
const workspace = new Workspace({
id: faker.datatype.uuid(),
})
.register(AffineSchemas)
.register(__unstableSchemas);
const binary = await workspaceApis.downloadPublicWorkspacePage(
id,
'page0'
);
applyUpdate(workspace.doc, new Uint8Array(binary));
expect(workspace.getPage('page0')).not.toBeNull();
expect(workspace.getPage('page1')).toBeNull();
}
{
const workspace = new Workspace({
id: faker.datatype.uuid(),
})
.register(AffineSchemas)
.register(__unstableSchemas);
const binary = await workspaceApis.downloadPublicWorkspacePage(
id,
'page1'
);
applyUpdate(workspace.doc, new Uint8Array(binary));
expect(workspace.getPage('page0')).toBeNull();
expect(workspace.getPage('page1')).not.toBeNull();
}
});
test(
'usage',
async () => {

View File

@@ -233,13 +233,16 @@ export function createWorkspaceApis(prefixUrl = '/') {
throw new RequestError(MessageCode.getMembersFailed, e);
});
},
createWorkspace: async (encodedYDoc: Blob): Promise<{ id: string }> => {
createWorkspace: async (
encodedYDoc: ArrayBuffer
): Promise<{ id: string }> => {
const auth = getLoginStorage();
assertExists(auth);
return fetch(prefixUrl + 'api/workspace', {
method: 'POST',
body: encodedYDoc,
headers: {
'Content-Type': 'application/octet-stream',
Authorization: auth.token,
},
})
@@ -382,6 +385,17 @@ export function createWorkspaceApis(prefixUrl = '/') {
throw new RequestError(MessageCode.leaveWorkspaceFailed, e);
});
},
downloadPublicWorkspacePage: async (
workspaceId: string,
pageId: string
): Promise<ArrayBuffer> => {
return fetch(
prefixUrl + `api/public/workspace/${workspaceId}/${pageId}`,
{
method: 'GET',
}
).then(r => r.arrayBuffer());
},
downloadWorkspace: async (
workspaceId: string,
published = false