test: main data source e2e test (#3816)
* test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: main data source e2e test * test: test * test: test * refactor: code improve * test: test
This commit is contained in:
parent
467699cdf9
commit
44e7f48f99
packages
core
plugins/@nocobase
plugin-collection-manager/src/client/__e2e__/collection-template
plugin-data-source-manager/src
plugin-mock-collections
@ -17,7 +17,7 @@ export class UUIDFieldInterface extends CollectionFieldInterface {
|
||||
'x-validator': 'uuid',
|
||||
},
|
||||
};
|
||||
availableTypes = ['string', 'uid'];
|
||||
availableTypes = ['string', 'uid', 'uuid'];
|
||||
properties = {
|
||||
'uiSchema.title': {
|
||||
type: 'string',
|
||||
|
@ -53,7 +53,6 @@ export interface CollectionSetting {
|
||||
* @default false
|
||||
*/
|
||||
inherit?: boolean;
|
||||
inherits?: string[];
|
||||
category?: any[];
|
||||
hidden?: boolean;
|
||||
description?: string;
|
||||
@ -120,6 +119,23 @@ interface AclRoleSetting {
|
||||
key?: string;
|
||||
//菜单权限配置
|
||||
menuUiSchemas?: string[];
|
||||
dataSourceKey?: string;
|
||||
}
|
||||
|
||||
interface DatabaseSetting {
|
||||
database: string;
|
||||
host: string;
|
||||
port: string;
|
||||
schema?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
interface DataSourceSetting {
|
||||
key: string;
|
||||
displayName: string;
|
||||
type: string;
|
||||
options: DatabaseSetting;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface PageConfig {
|
||||
@ -229,6 +245,15 @@ interface ExtendUtils {
|
||||
* 更新角色权限配置
|
||||
*/
|
||||
updateRole: <T = any>(roleSetting: AclRoleSetting) => Promise<T>;
|
||||
/**
|
||||
* 创建一个外部数据源(pg)
|
||||
*/
|
||||
mockExternalDataSource: <T = any>(DataSourceSetting: DataSourceSetting) => Promise<T>;
|
||||
/**
|
||||
* 删除外部数据源
|
||||
* @param key 外部数据源key
|
||||
*/
|
||||
destoryExternalDataSource: <T = any>(key: string) => Promise<T>;
|
||||
}
|
||||
|
||||
const PORT = process.env.APP_PORT || 20000;
|
||||
@ -437,6 +462,20 @@ const _test = base.extend<ExtendUtils>({
|
||||
|
||||
await use(updateRole);
|
||||
},
|
||||
mockExternalDataSource: async ({ browser }, use) => {
|
||||
const mockExternalDataSource = async (DataSourceSetting: DataSourceSetting) => {
|
||||
return createExternalDataSource(DataSourceSetting);
|
||||
};
|
||||
|
||||
await use(mockExternalDataSource);
|
||||
},
|
||||
destoryExternalDataSource: async ({ browser }, use) => {
|
||||
const destoryDataSource = async (key: string) => {
|
||||
return destoryExternalDataSource(key);
|
||||
};
|
||||
|
||||
await use(destoryDataSource);
|
||||
},
|
||||
});
|
||||
|
||||
export const test = Object.assign(_test, {
|
||||
@ -581,10 +620,6 @@ const deleteCollections = async (collectionNames: string[]) => {
|
||||
|
||||
const result = await api.post(`/api/collections:destroy?${params}`, {
|
||||
headers,
|
||||
params: {
|
||||
// 自动删除依赖于集合的对象(如视图),进而删除依赖于这些对象的所有对象
|
||||
cascade: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.ok()) {
|
||||
@ -696,8 +731,12 @@ const updateRole = async (roleSetting: AclRoleSetting) => {
|
||||
const state = await api.storageState();
|
||||
const headers = getHeaders(state);
|
||||
const name = roleSetting.name;
|
||||
const dataSourceKey = roleSetting.dataSourceKey;
|
||||
|
||||
const result = await api.post(`/api/roles:update?filterByTk=${name}`, {
|
||||
const url = !dataSourceKey
|
||||
? `/api/roles:update?filterByTk=${name}`
|
||||
: `/api/dataSources/${dataSourceKey}/roles:update?filterByTk=${name}`;
|
||||
const result = await api.post(url, {
|
||||
headers,
|
||||
data: { ...roleSetting },
|
||||
});
|
||||
@ -724,7 +763,48 @@ const setDefaultRole = async (name) => {
|
||||
data: { roleName: name },
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 创建外部数据源
|
||||
* @paramn
|
||||
*/
|
||||
const createExternalDataSource = async (dataSourceSetting: DataSourceSetting) => {
|
||||
const api = await request.newContext({
|
||||
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||
});
|
||||
const state = await api.storageState();
|
||||
const headers = getHeaders(state);
|
||||
const result = await api.post(`/api/dataSources:create`, {
|
||||
headers,
|
||||
data: { ...dataSourceSetting },
|
||||
});
|
||||
|
||||
if (!result.ok()) {
|
||||
throw new Error(await result.text());
|
||||
}
|
||||
const dataSourceData = (await result.json()).data;
|
||||
return dataSourceData;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除外部数据源
|
||||
* @paramn
|
||||
*/
|
||||
const destoryExternalDataSource = async (key) => {
|
||||
const api = await request.newContext({
|
||||
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||
});
|
||||
const state = await api.storageState();
|
||||
const headers = getHeaders(state);
|
||||
const result = await api.post(`/api/dataSources:destroy?filterByTk=${key}`, {
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!result.ok()) {
|
||||
throw new Error(await result.text());
|
||||
}
|
||||
const dataSourceData = (await result.json()).data;
|
||||
return dataSourceData;
|
||||
};
|
||||
/**
|
||||
* 根据 collection 的配置生成 Faker 数据
|
||||
* @param collectionSetting
|
||||
|
@ -97,6 +97,238 @@ test.describe('create collection', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// //预设字段
|
||||
test.describe('create collection with preset fields', () => {
|
||||
test('all preset fields by default', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//默认提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: true,
|
||||
createdAt: true,
|
||||
createdBy: true,
|
||||
updatedAt: true,
|
||||
updatedBy: true,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'id',
|
||||
type: 'bigInt',
|
||||
// 其他属性
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'createdAt',
|
||||
type: 'date',
|
||||
// 其他属性
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'createdBy',
|
||||
type: 'belongsTo',
|
||||
// 其他属性
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'updatedBy',
|
||||
type: 'belongsTo',
|
||||
// 其他属性
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'updatedAt',
|
||||
type: 'date',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
test('id preset field', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
await page.getByRole('row', { name: 'ID Integer Primary key' }).locator('.ant-checkbox-input').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: true,
|
||||
createdAt: false,
|
||||
createdBy: false,
|
||||
updatedAt: false,
|
||||
updatedBy: false,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'id',
|
||||
type: 'bigInt',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
test('createdAt preset field', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
await page.getByRole('row', { name: 'Created at' }).locator('.ant-checkbox-input').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: false,
|
||||
createdAt: true,
|
||||
createdBy: false,
|
||||
updatedAt: false,
|
||||
updatedBy: false,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'createdAt',
|
||||
type: 'date',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
test('createdBy preset field', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
await page.getByRole('row', { name: 'created By' }).locator('.ant-checkbox-input').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: false,
|
||||
createdAt: false,
|
||||
createdBy: true,
|
||||
updatedAt: false,
|
||||
updatedBy: false,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'createdBy',
|
||||
type: 'belongsTo',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
test('updatedBy preset field', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
await page.getByRole('row', { name: 'Last updated by' }).locator('.ant-checkbox-input').check();
|
||||
//添加关系字段,断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: false,
|
||||
createdAt: false,
|
||||
createdBy: false,
|
||||
updatedAt: false,
|
||||
updatedBy: true,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'updatedBy',
|
||||
type: 'belongsTo',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
test('updatedAt preset field', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
await page.getByRole('row', { name: 'Last updated at' }).locator('.ant-checkbox-input').check();
|
||||
//添加关系字段,断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: false,
|
||||
createdAt: false,
|
||||
createdBy: false,
|
||||
updatedAt: true,
|
||||
updatedBy: false,
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'updatedAt',
|
||||
type: 'date',
|
||||
// 其他属性
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
test('unselect preset fields', async ({ page }) => {
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByRole('button', { name: 'plus Create collection down' }).click();
|
||||
await page.getByRole('menuitem', { name: 'General collection' }).locator('span').click();
|
||||
await page.getByLabel('block-item-Input-collections-Collection display name').getByRole('textbox').fill(uid());
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().hover();
|
||||
await page.locator('.ant-drawer-content-wrapper .ant-table-container .ant-table-selection-column').first().click();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('api/collections:create')),
|
||||
page.getByLabel('action-Action-Submit').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
autoGenId: false,
|
||||
createdAt: false,
|
||||
createdBy: false,
|
||||
updatedAt: false,
|
||||
updatedBy: false,
|
||||
fields: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('configure fields', () => {
|
||||
test('basic', async ({ page, mockCollections }) => {
|
||||
test.slow();
|
||||
@ -261,6 +493,148 @@ test.describe('configure fields', () => {
|
||||
});
|
||||
});
|
||||
|
||||
//创建非Id为主键/唯一索引
|
||||
test.describe('create primary key or unique index other than ID.', () => {
|
||||
test('integer field as primary key', async ({ page, mockCollection }) => {
|
||||
const name = uid();
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await mockCollection({
|
||||
name: name,
|
||||
autoGenId: false,
|
||||
fields: [],
|
||||
});
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(name);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${name}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Integer' }).click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').fill(uid());
|
||||
await page.getByLabel('Primary').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('/fields:create')),
|
||||
page.getByLabel('action-Action-Submit-fields-').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
unique: false,
|
||||
interface: 'integer',
|
||||
primaryKey: true,
|
||||
type: 'bigInt',
|
||||
});
|
||||
});
|
||||
test('integer field set unique index', async ({ page, mockCollection }) => {
|
||||
const name = uid();
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await mockCollection({
|
||||
name: name,
|
||||
autoGenId: false,
|
||||
fields: [],
|
||||
});
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(name);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${name}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Integer' }).click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').fill(uid());
|
||||
// Primary 和 Unique选中一个,其中一个自动取消勾选
|
||||
await page.getByLabel('Primary').check();
|
||||
await page.getByLabel('Unique').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('/fields:create')),
|
||||
page.getByLabel('action-Action-Submit-fields-').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
unique: true,
|
||||
interface: 'integer',
|
||||
primaryKey: false,
|
||||
type: 'bigInt',
|
||||
});
|
||||
});
|
||||
test('input(string) field set unique index', async ({ page, mockCollection }) => {
|
||||
const name = uid();
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await mockCollection({
|
||||
name: name,
|
||||
autoGenId: false,
|
||||
fields: [],
|
||||
});
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(name);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${name}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Single line text' }).click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').fill(uid());
|
||||
// Primary 和 Unique选中一个,其中一个自动取消勾选
|
||||
await page.getByLabel('Primary').check();
|
||||
await page.getByLabel('Unique').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('/fields:create')),
|
||||
page.getByLabel('action-Action-Submit-fields-').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
unique: true,
|
||||
interface: 'input',
|
||||
primaryKey: false,
|
||||
type: 'string',
|
||||
});
|
||||
});
|
||||
test('input(string) field set as primary key', async ({ page, mockCollection }) => {
|
||||
const name = uid();
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await mockCollection({
|
||||
name: name,
|
||||
autoGenId: false,
|
||||
fields: [],
|
||||
});
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(name);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${name}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Single line text' }).click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').click();
|
||||
await page.getByLabel('block-item-Input-fields-Field display name').getByRole('textbox').fill(uid());
|
||||
// Primary 和 Unique选中一个,其中一个自动取消勾选
|
||||
await page.getByLabel('Unique').check();
|
||||
await page.getByLabel('Primary').check();
|
||||
//断言提交的data是否符合预期
|
||||
const [request] = await Promise.all([
|
||||
page.waitForRequest((request) => request.url().includes('/fields:create')),
|
||||
page.getByLabel('action-Action-Submit-fields-').click(),
|
||||
]);
|
||||
const postData = request.postDataJSON();
|
||||
//提交的数据符合预期
|
||||
expect(postData).toMatchObject({
|
||||
unique: false,
|
||||
interface: 'input',
|
||||
primaryKey: true,
|
||||
type: 'string',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('edit', () => {
|
||||
test('basic', async ({ page, mockCollection }) => {
|
||||
const collectionDisplayName = uid();
|
||||
@ -283,3 +657,291 @@ test.describe('edit', () => {
|
||||
await expect(page.getByRole('cell', { name: newDescription, exact: true })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
//创建关系字段
|
||||
//sourceKey, targetKey, optional field types are [string ',' bigInt ',' integer ',' uuid ',' uid '] and are non primary key field with a Unique index set
|
||||
test.describe('association constraints support selecting non-primary key fields with Unique indexes', () => {
|
||||
const fields = [
|
||||
{ name: 'id', interface: 'id', type: 'bigInt' },
|
||||
{ name: 'string', interface: 'input', type: 'string', unique: true },
|
||||
{ name: 'bigInt', type: 'bigInt', interface: 'integer', unique: true },
|
||||
{ name: 'integer', type: 'integer', interface: 'input', unique: true },
|
||||
{ name: 'uuid', type: 'uuid', interface: 'input', unique: true },
|
||||
{ name: 'uid', type: 'uid', interface: 'input', unique: true },
|
||||
{ name: 'string1', interface: 'input', type: 'string' },
|
||||
{ name: 'bigInt1', type: 'bigInt', interface: 'integer' },
|
||||
{ name: 'integer1', type: 'integer', interface: 'input' },
|
||||
{ name: 'uuid1', type: 'uuid', interface: 'input' },
|
||||
{ name: 'uid1', type: 'uid', interface: 'input' },
|
||||
];
|
||||
test('oho sourceKey', async ({ page, mockCollection }) => {
|
||||
const collectionName = uid();
|
||||
await mockCollection({
|
||||
name: collectionName,
|
||||
autoGenId: true,
|
||||
fields,
|
||||
});
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(collectionName);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${collectionName}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'One to one (has one)' }).click();
|
||||
await page.getByLabel('block-item-SourceKey-fields-').click();
|
||||
// 获取所有选项的文本内容
|
||||
const options = await page.locator('.rc-virtual-list').evaluate(() => {
|
||||
const optionElements = document.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期
|
||||
expect(options).toEqual(expect.arrayContaining(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']));
|
||||
});
|
||||
test('obo targetKey', async ({ page, mockCollection }) => {
|
||||
const collectionName = uid();
|
||||
await mockCollection({
|
||||
name: collectionName,
|
||||
autoGenId: true,
|
||||
fields,
|
||||
});
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(collectionName);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${collectionName}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'One to one (belongs to)' }).click();
|
||||
await page.getByLabel('block-item-Select-fields-Target collection').click();
|
||||
await page.getByRole('option', { name: collectionName }).locator('div').click();
|
||||
await page.getByLabel('block-item-TargetKey-fields-').click();
|
||||
// 获取所有选项的文本内容
|
||||
const options = await page
|
||||
.locator('.rc-virtual-list')
|
||||
.nth(1)
|
||||
.evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期
|
||||
expect(options).toEqual(expect.arrayContaining(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']));
|
||||
});
|
||||
test('o2m targetKey & sourceKey', async ({ page, mockCollection }) => {
|
||||
const collectionName = uid();
|
||||
|
||||
await mockCollection({
|
||||
name: collectionName,
|
||||
autoGenId: true,
|
||||
fields,
|
||||
});
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(collectionName);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${collectionName}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'One to many' }).click();
|
||||
|
||||
await page.getByLabel('block-item-SourceKey-fields-').click();
|
||||
// sourceKey 选项符合预期
|
||||
const sourcekeyOptions = await page.locator('.rc-virtual-list').evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期
|
||||
expect(sourcekeyOptions).toEqual(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']);
|
||||
|
||||
await page.getByLabel('block-item-Select-fields-Target collection').click();
|
||||
await page.getByRole('option', { name: collectionName }).locator('div').click();
|
||||
await page.getByLabel('block-item-TargetKey-fields-').click();
|
||||
|
||||
//targetKey 选项符合预期
|
||||
const targetKeyOptions = await page
|
||||
.locator('.rc-virtual-list')
|
||||
.nth(2)
|
||||
.evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期,o2m的targetkey 不限制unique
|
||||
expect(targetKeyOptions).toEqual(
|
||||
expect.arrayContaining([
|
||||
'ID',
|
||||
'string',
|
||||
'bigInt',
|
||||
'integer',
|
||||
'uuid',
|
||||
'uid',
|
||||
'string1',
|
||||
'bigInt1',
|
||||
'integer1',
|
||||
'uuid1',
|
||||
'uid1',
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('m2o targetKey & foreignKey', async ({ page, mockCollection }) => {
|
||||
const collectionName = uid();
|
||||
const foreignKey = `f_${uid()}`;
|
||||
await mockCollection({
|
||||
name: collectionName,
|
||||
autoGenId: true,
|
||||
fields,
|
||||
});
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(collectionName);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${collectionName}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Many to one' }).click();
|
||||
|
||||
await page.getByLabel('block-item-ForeignKey-fields-').locator('input').click();
|
||||
// ForeignKey 选项符合预期
|
||||
const foreignKeyOptions = await page.locator('.rc-virtual-list').evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期
|
||||
expect(foreignKeyOptions).toEqual(
|
||||
expect.arrayContaining([
|
||||
'ID',
|
||||
'string',
|
||||
'bigInt',
|
||||
'integer',
|
||||
'uuid',
|
||||
'uid',
|
||||
'string1',
|
||||
'bigInt1',
|
||||
'integer1',
|
||||
'uuid1',
|
||||
'uid1',
|
||||
]),
|
||||
);
|
||||
//ForeignKey 支持自定义输入和选择
|
||||
await page.getByLabel('block-item-ForeignKey-fields-').locator('input').clear();
|
||||
await page.getByLabel('block-item-ForeignKey-fields-').locator('input').fill(foreignKey);
|
||||
|
||||
const inputValue = await page.getByLabel('block-item-ForeignKey-fields-').locator('input').inputValue();
|
||||
expect(inputValue).toEqual(foreignKey);
|
||||
await page.getByRole('option', { name: 'uuid1' }).locator('div').click();
|
||||
const optionValue = await page.getByLabel('block-item-ForeignKey-fields-').locator('input').inputValue();
|
||||
expect(optionValue).toEqual('uuid1');
|
||||
|
||||
await page.getByLabel('block-item-Select-fields-Target collection').click();
|
||||
await page.getByRole('option', { name: collectionName }).locator('div').click();
|
||||
await page.getByLabel('block-item-TargetKey-fields-').click();
|
||||
|
||||
//targetKey 选项符合预期
|
||||
const targetKeyOptions = await page
|
||||
.locator('.rc-virtual-list')
|
||||
.nth(2)
|
||||
.evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期, 限制unique
|
||||
expect(targetKeyOptions).toEqual(expect.arrayContaining(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']));
|
||||
});
|
||||
|
||||
test('m2m sourceKey & foreignKey & targetKey', async ({ page, mockCollection }) => {
|
||||
const collectionName = uid();
|
||||
const foreignKey = `f_${uid()}`;
|
||||
await mockCollection({
|
||||
name: collectionName,
|
||||
autoGenId: true,
|
||||
fields,
|
||||
});
|
||||
await page.goto('/admin/settings/data-source-manager/list');
|
||||
await page.getByRole('button', { name: 'Configure' }).first().click();
|
||||
await page.getByLabel('action-Filter.Action-Filter-').click();
|
||||
await page.getByRole('textbox').nth(1).click();
|
||||
await page.getByRole('textbox').nth(1).fill(collectionName);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
await page.getByLabel(`action-Action.Link-Configure fields-collections-${collectionName}`).click();
|
||||
await page.getByRole('button', { name: 'plus Add field' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Many to many' }).click();
|
||||
|
||||
await page.getByLabel('block-item-SourceKey-fields-').click();
|
||||
// sourceKey 选项符合预期
|
||||
const sourcekeyOptions = await page.locator('.rc-virtual-list').evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
expect(sourcekeyOptions).toEqual(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']);
|
||||
|
||||
// ForeignKey1 选项符合预期
|
||||
await page.getByLabel('block-item-ThroughCollection-').click();
|
||||
await page.getByRole('option', { name: collectionName }).locator('div').click();
|
||||
await page.getByLabel('block-item-ForeignKey-fields-Foreign key 1').locator('input').click();
|
||||
const foreignKeyOptions = await page
|
||||
.locator('.rc-virtual-list')
|
||||
.nth(2)
|
||||
.evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期
|
||||
expect(foreignKeyOptions).toEqual(
|
||||
expect.arrayContaining([
|
||||
'ID',
|
||||
'string',
|
||||
'bigInt',
|
||||
'integer',
|
||||
'uuid',
|
||||
'uid',
|
||||
'string1',
|
||||
'bigInt1',
|
||||
'integer1',
|
||||
'uuid1',
|
||||
'uid1',
|
||||
]),
|
||||
);
|
||||
//ForeignKey1 支持自定义输入和选择
|
||||
await page.getByLabel('block-item-ForeignKey-fields-Foreign key 1').locator('input').clear();
|
||||
await page.getByLabel('block-item-ForeignKey-fields-Foreign key 1').locator('input').fill(foreignKey);
|
||||
|
||||
const inputValue = await page
|
||||
.getByLabel('block-item-ForeignKey-fields-Foreign key 1')
|
||||
.locator('input')
|
||||
.inputValue();
|
||||
expect(inputValue).toEqual(foreignKey);
|
||||
await page.getByRole('option', { name: 'uuid1' }).locator('div').click();
|
||||
const optionValue = await page
|
||||
.getByLabel('block-item-ForeignKey-fields-Foreign key 1')
|
||||
.locator('input')
|
||||
.inputValue();
|
||||
expect(optionValue).toEqual('uuid1');
|
||||
|
||||
await page.getByLabel('block-item-Select-fields-Target collection').click();
|
||||
await page.getByRole('option', { name: collectionName }).locator('div').click();
|
||||
await page.getByLabel('block-item-TargetKey-fields-').click();
|
||||
|
||||
//targetKey 选项符合预期
|
||||
const targetKeyOptions = await page
|
||||
.locator('.rc-virtual-list')
|
||||
.nth(2)
|
||||
.evaluate((element) => {
|
||||
const optionElements = element.querySelectorAll('.ant-select-item-option');
|
||||
return Array.from(optionElements).map((option) => option.textContent);
|
||||
});
|
||||
|
||||
// 断言下拉列表是否符合预期, 限制unique
|
||||
expect(targetKeyOptions).toEqual(expect.arrayContaining(['ID', 'string', 'bigInt', 'integer', 'uuid', 'uid']));
|
||||
});
|
||||
});
|
||||
|
@ -38,6 +38,9 @@ export const CollectionFieldInterfaceSelect = observer(
|
||||
</Tag>
|
||||
) : (
|
||||
<Select
|
||||
aria-label={`field-interface-${record?.type}`}
|
||||
//@ts-ignore
|
||||
role="button"
|
||||
defaultValue={value}
|
||||
style={{ width: '100%' }}
|
||||
popupMatchSelectWidth={false}
|
||||
|
@ -38,7 +38,9 @@ export const FieldTitleInput = observer(
|
||||
useEffect(() => {
|
||||
setTitleValue(value);
|
||||
}, [value]);
|
||||
return <Input value={titleValue} onChange={handleChange} style={{ minWidth: '100px' }} />;
|
||||
return (
|
||||
<Input value={titleValue} onChange={handleChange} style={{ minWidth: '100px' }} aria-label="field-title-input" />
|
||||
);
|
||||
},
|
||||
{ displayName: 'FieldTitleInput' },
|
||||
);
|
||||
|
@ -12,6 +12,9 @@ export const FieldType = observer(
|
||||
<Tag>{value}</Tag>
|
||||
) : (
|
||||
<Select
|
||||
aria-label={`field-type-${value}`}
|
||||
//@ts-ignore
|
||||
role="button"
|
||||
defaultValue={value}
|
||||
popupMatchSelectWidth={false}
|
||||
style={{ width: '100%' }}
|
||||
|
@ -31,6 +31,8 @@ export const ViewDatabaseConnectionAction = () => {
|
||||
onClick={() => {
|
||||
navigate(getConnectionCollectionPath(record.key));
|
||||
}}
|
||||
role="button"
|
||||
aria-label={`${record?.key}-Configure`}
|
||||
>
|
||||
{t('Configure')}
|
||||
</Button>
|
||||
|
@ -125,6 +125,33 @@ describe('data source with acl', () => {
|
||||
expect(postRes.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should update roles resources', async () => {
|
||||
const adminUser = await app.db.getRepository('users').create({
|
||||
values: {
|
||||
roles: ['root'],
|
||||
},
|
||||
});
|
||||
|
||||
const adminAgent: any = app.agent().login(adminUser);
|
||||
|
||||
await adminAgent.resource('dataSources.roles', 'mockInstance1').update({
|
||||
filterByTk: 'member',
|
||||
values: {
|
||||
resources: [
|
||||
{
|
||||
posts: {
|
||||
actions: {
|
||||
view: {
|
||||
fields: ['title'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should set main data source strategy', async () => {
|
||||
const adminUser = await app.db.getRepository('users').create({
|
||||
values: {
|
||||
|
@ -26,6 +26,25 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
if (params.values.resources) {
|
||||
await ctx.db.getRepository('dataSourcesRolesResources').destroy({
|
||||
filter: {
|
||||
roleName: name,
|
||||
dataSourceKey,
|
||||
},
|
||||
});
|
||||
|
||||
for (const resource of params.values.resources) {
|
||||
await ctx.db.getRepository('dataSourcesRolesResources').create({
|
||||
values: {
|
||||
...resource,
|
||||
roleName: name,
|
||||
dataSourceKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = connectionRoleRecord.toJSON();
|
||||
|
||||
await next();
|
||||
|
@ -13,5 +13,8 @@
|
||||
"@nocobase/test": "0.x",
|
||||
"@nocobase/utils": "0.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"pg": "^8.11.3"
|
||||
},
|
||||
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
|
||||
}
|
||||
|
@ -0,0 +1,175 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- 创建 users 表
|
||||
CREATE TABLE users (
|
||||
user_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
username VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
-- 创建 profiles 表
|
||||
CREATE TABLE profiles (
|
||||
profile_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
address VARCHAR(255),
|
||||
user_uuid UUID REFERENCES users(user_uuid)
|
||||
);
|
||||
|
||||
-- 创建 roles 表
|
||||
CREATE TABLE roles (
|
||||
role_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
role_name VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
-- 创建 user_roles 关联表
|
||||
CREATE TABLE user_roles (
|
||||
user_uuid UUID REFERENCES users(user_uuid),
|
||||
role_uuid UUID REFERENCES roles(role_uuid),
|
||||
PRIMARY KEY (user_uuid, role_uuid)
|
||||
);
|
||||
|
||||
|
||||
-- 创建 orders 表
|
||||
CREATE TABLE orders (
|
||||
order_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_uuid UUID REFERENCES users(user_uuid),
|
||||
order_name VARCHAR(255) NOT NULL
|
||||
-- ... 其他字段
|
||||
);
|
||||
|
||||
-- 向 users 表插入测试数据
|
||||
INSERT INTO users (username) VALUES
|
||||
('Alice'),
|
||||
('Bob');
|
||||
|
||||
INSERT INTO orders (user_uuid, order_name) VALUES
|
||||
((SELECT user_uuid FROM users WHERE username = 'Alice'), 'Order1'),
|
||||
((SELECT user_uuid FROM users WHERE username = 'Bob'), 'Order2');
|
||||
|
||||
|
||||
-- 向 profiles 表插入测试数据
|
||||
INSERT INTO profiles (address, user_uuid) VALUES
|
||||
('123 Main St', (SELECT user_uuid FROM users WHERE username = 'Alice')),
|
||||
('456 High St', (SELECT user_uuid FROM users WHERE username = 'Bob'));
|
||||
|
||||
-- 向 roles 表插入测试数据
|
||||
INSERT INTO roles (role_name) VALUES
|
||||
('Admin'),
|
||||
('User');
|
||||
|
||||
-- 建立 users 和 roles 之间的关系
|
||||
INSERT INTO user_roles (user_uuid, role_uuid) VALUES
|
||||
((SELECT user_uuid FROM users WHERE username = 'Alice'), (SELECT role_uuid FROM roles WHERE role_name = 'Admin')),
|
||||
((SELECT user_uuid FROM users WHERE username = 'Bob'), (SELECT role_uuid FROM roles WHERE role_name = 'User'));
|
||||
|
||||
|
||||
CREATE TABLE "test_table" (
|
||||
"id2" SERIAL PRIMARY KEY,
|
||||
"smallint" int2,
|
||||
"integer" int4,
|
||||
"bigint" int8,
|
||||
"boolean" bool,
|
||||
"numeric" numeric,
|
||||
"real" float4,
|
||||
"double" float8,
|
||||
"money" money,
|
||||
"date" date,
|
||||
"time" time,
|
||||
"timestamp" timestamp,
|
||||
"timestamptz" timestamptz,
|
||||
"interval" interval,
|
||||
"char" bpchar,
|
||||
"varchar" varchar,
|
||||
"text" text,
|
||||
"tsquery" tsquery,
|
||||
"tsvector" tsvector,
|
||||
"uuid" uuid,
|
||||
"xml" xml,
|
||||
"json" json,
|
||||
"jsonb" jsonb,
|
||||
"bit" bit,
|
||||
"bitvarying" varbit,
|
||||
"bytea" bytea,
|
||||
"cidr" cidr,
|
||||
"inet" inet,
|
||||
"macaddr" macaddr,
|
||||
"txidsnapshot" txid_snapshot,
|
||||
"box" box,
|
||||
"circle" circle,
|
||||
"line" line,
|
||||
"lseg" lseg,
|
||||
"path" path,
|
||||
"point" point,
|
||||
"polygon" polygon
|
||||
);
|
||||
|
||||
|
||||
-- 创建 authors 表
|
||||
CREATE TABLE authors (
|
||||
author_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
author_name VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
-- 创建 posts 表
|
||||
CREATE TABLE posts (
|
||||
post_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
post_title VARCHAR(100) NOT NULL,
|
||||
post_content TEXT NOT NULL,
|
||||
author_id UUID REFERENCES authors(author_id)
|
||||
);
|
||||
|
||||
-- 创建 comments 表
|
||||
CREATE TABLE comments (
|
||||
comment_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
comment_content TEXT NOT NULL,
|
||||
post_id UUID REFERENCES posts(post_id),
|
||||
author_id UUID REFERENCES authors(author_id)
|
||||
);
|
||||
|
||||
-- 创建 tags 表
|
||||
CREATE TABLE tags (
|
||||
tag_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tag_name VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
-- 创建 post_tags 关联表
|
||||
CREATE TABLE post_tags (
|
||||
post_id UUID REFERENCES posts(post_id),
|
||||
tag_id UUID REFERENCES tags(tag_id),
|
||||
PRIMARY KEY (post_id, tag_id)
|
||||
);
|
||||
|
||||
-- 向 authors 表插入测试数据
|
||||
INSERT INTO authors (author_name) VALUES
|
||||
('Author1'),
|
||||
('Author2');
|
||||
|
||||
-- 向 posts 表插入测试数据
|
||||
INSERT INTO posts (post_title, post_content, author_id) VALUES
|
||||
('Post1', 'This is the content of Post1', (SELECT author_id FROM authors WHERE author_name = 'Author1')),
|
||||
('Post2', 'This is the content of Post2', (SELECT author_id FROM authors WHERE author_name = 'Author2'));
|
||||
|
||||
-- 向 comments 表插入测试数据
|
||||
INSERT INTO comments (comment_content, post_id, author_id) VALUES
|
||||
('This is a comment on Post1', (SELECT post_id FROM posts WHERE post_title = 'Post1'), (SELECT author_id FROM authors WHERE author_name = 'Author1')),
|
||||
('This is a comment on Post2', (SELECT post_id FROM posts WHERE post_title = 'Post2'), (SELECT author_id FROM authors WHERE author_name = 'Author2'));
|
||||
|
||||
-- 向 tags 表插入测试数据
|
||||
INSERT INTO tags (tag_name) VALUES
|
||||
('Tag1'),
|
||||
('Tag2');
|
||||
|
||||
-- 建立 posts 和 tags 之间的关系
|
||||
INSERT INTO post_tags (post_id, tag_id) VALUES
|
||||
((SELECT post_id FROM posts WHERE post_title = 'Post1'), (SELECT tag_id FROM tags WHERE tag_name = 'Tag1')),
|
||||
((SELECT post_id FROM posts WHERE post_title = 'Post2'), (SELECT tag_id FROM tags WHERE tag_name = 'Tag2'));
|
||||
|
||||
|
||||
-- 创建无主键的表 test_table_no_pk
|
||||
CREATE TABLE test_table_no_pk (
|
||||
column1 VARCHAR(100),
|
||||
column2 INT
|
||||
);
|
||||
|
||||
-- 向 test_table_no_pk 表插入测试数据
|
||||
INSERT INTO test_table_no_pk (column1, column2) VALUES
|
||||
('TestData1', 1),
|
||||
('TestData2', 2);
|
@ -1,11 +1,14 @@
|
||||
import Database, { Collection, Repository } from '@nocobase/database';
|
||||
import { CollectionRepository } from '@nocobase/plugin-collection-manager';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import { InstallOptions, Plugin } from '@nocobase/server';
|
||||
import { merge, uid } from '@nocobase/utils';
|
||||
import _ from 'lodash';
|
||||
import collectionTemplates from './collection-templates';
|
||||
import * as fieldInterfaces from './field-interfaces';
|
||||
import { mockAttachment } from './field-interfaces';
|
||||
import { Client } from 'pg';
|
||||
import path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
export class PluginMockCollectionsServer extends Plugin {
|
||||
async load() {
|
||||
@ -304,6 +307,41 @@ export class PluginMockCollectionsServer extends Plugin {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async install(options?: InstallOptions) {
|
||||
const dbName = this.app.db.options.database;
|
||||
const externalDB = `${dbName}_external_test`;
|
||||
|
||||
// create external database
|
||||
let client = new Client({
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
host: process.env.DB_HOST,
|
||||
database: process.env.DB_DATABASE,
|
||||
port: parseInt(process.env.DB_PORT),
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
await client.query(`DROP DATABASE IF EXISTS ${externalDB}`);
|
||||
await client.query(`CREATE DATABASE ${externalDB}`);
|
||||
await client.end();
|
||||
|
||||
// import sql import external database
|
||||
client = new Client({
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
host: process.env.DB_HOST,
|
||||
database: externalDB,
|
||||
port: parseInt(process.env.DB_PORT),
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
const sqlFile = path.resolve(__dirname, './external.sql');
|
||||
const sql: string = await fs.readFile(sqlFile, 'utf8');
|
||||
await client.query(sql);
|
||||
await client.end();
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginMockCollectionsServer;
|
||||
|
Loading…
Reference in New Issue
Block a user