mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 11:56:29 +00:00
Merge branch 'main' into T-4256
This commit is contained in:
commit
4aea20dafb
10
.github/workflows/e2e.yml
vendored
10
.github/workflows/e2e.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
env:
|
env:
|
||||||
__E2E__: true # e2e will be reusing this workflow, so we need to set this flag to true
|
__E2E__: true # e2e will be reusing this workflow, so we need to set this flag to true
|
||||||
@ -104,7 +104,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn
|
||||||
|
|
||||||
- name: Download build artifact
|
- name: Download build artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@ -184,7 +184,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn
|
||||||
|
|
||||||
- name: Download build artifact
|
- name: Download build artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@ -264,7 +264,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn
|
||||||
|
|
||||||
- name: Download build artifact
|
- name: Download build artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@ -310,7 +310,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn --frozen-lockfile
|
- run: yarn
|
||||||
|
|
||||||
- name: Download e2e report artifact
|
- name: Download e2e report artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
@ -211,6 +211,7 @@ export const AddCollectionAction = (props) => {
|
|||||||
record,
|
record,
|
||||||
showReverseFieldConfig: true,
|
showReverseFieldConfig: true,
|
||||||
presetFieldsDisabled: currentTemplate?.presetFieldsDisabled,
|
presetFieldsDisabled: currentTemplate?.presetFieldsDisabled,
|
||||||
|
presetFieldsDisabledIncludes: currentTemplate?.presetFieldsDisabledIncludes,
|
||||||
...scope,
|
...scope,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -204,7 +204,7 @@ export const PresetFields = observer(
|
|||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
getCheckboxProps: (record) => ({
|
getCheckboxProps: (record) => ({
|
||||||
name: record.name,
|
name: record.name,
|
||||||
disabled: props?.disabled,
|
disabled: props?.disabled || props?.presetFieldsDisabledIncludes?.includes?.(record.name),
|
||||||
}),
|
}),
|
||||||
onChange: (_, selectedRows) => {
|
onChange: (_, selectedRows) => {
|
||||||
const fields = getDefaultCollectionFields(selectedRows, form.values);
|
const fields = getDefaultCollectionFields(selectedRows, form.values);
|
||||||
|
@ -75,6 +75,7 @@ export const defaultConfigurableProperties = {
|
|||||||
'x-component': PresetFields,
|
'x-component': PresetFields,
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
disabled: '{{ presetFieldsDisabled }}',
|
disabled: '{{ presetFieldsDisabled }}',
|
||||||
|
presetFieldsDisabledIncludes: '{{presetFieldsDisabledIncludes}}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -72,6 +72,7 @@ export class TreeCollectionTemplate extends CollectionTemplate {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
presetFieldsDisabledIncludes = ['id'];
|
||||||
events = {
|
events = {
|
||||||
beforeSubmit(values) {
|
beforeSubmit(values) {
|
||||||
if (Array.isArray(values?.fields)) {
|
if (Array.isArray(values?.fields)) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Database } from '@nocobase/database';
|
import { Database, Repository } from '@nocobase/database';
|
||||||
import { MockServer, createMockServer } from '@nocobase/test';
|
import { MockServer, createMockServer } from '@nocobase/test';
|
||||||
import compose from 'koa-compose';
|
import compose from 'koa-compose';
|
||||||
import { parseBuilder, parseFieldAndAssociations, queryData } from '../actions/query';
|
import { parseBuilder, parseFieldAndAssociations, queryData } from '../actions/query';
|
||||||
@ -15,6 +15,7 @@ import { parseBuilder, parseFieldAndAssociations, queryData } from '../actions/q
|
|||||||
describe('api', () => {
|
describe('api', () => {
|
||||||
let app: MockServer;
|
let app: MockServer;
|
||||||
let db: Database;
|
let db: Database;
|
||||||
|
let repo: Repository;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await createMockServer({
|
app = await createMockServer({
|
||||||
@ -26,6 +27,10 @@ describe('api', () => {
|
|||||||
db.collection({
|
db.collection({
|
||||||
name: 'chart_test',
|
name: 'chart_test',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'bigInt',
|
||||||
|
name: 'id',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'double',
|
type: 'double',
|
||||||
name: 'price',
|
name: 'price',
|
||||||
@ -45,11 +50,11 @@ describe('api', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
await db.sync();
|
await db.sync();
|
||||||
const repo = db.getRepository('chart_test');
|
repo = db.getRepository('chart_test');
|
||||||
await repo.create({
|
await repo.create({
|
||||||
values: [
|
values: [
|
||||||
{ price: 1, count: 1, title: 'title1', createdAt: '2023-02-02' },
|
{ id: 1, price: 1, count: 1, title: 'title1', createdAt: '2023-02-02' },
|
||||||
{ price: 2, count: 2, title: 'title2', createdAt: '2023-01-01' },
|
{ id: 2, price: 2, count: 2, title: 'title2', createdAt: '2023-01-01' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -124,4 +129,63 @@ describe('api', () => {
|
|||||||
expect(ctx.action.params.values.data).toBeDefined();
|
expect(ctx.action.params.values.data).toBeDefined();
|
||||||
expect(ctx.action.params.values.data).toMatchObject([{ createdAt: '2023-01' }, { createdAt: '2023-02' }]);
|
expect(ctx.action.params.values.data).toMatchObject([{ createdAt: '2023-01' }, { createdAt: '2023-02' }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('datetime format with timezone', async () => {
|
||||||
|
const dialect = db.sequelize.getDialect();
|
||||||
|
if (dialect === 'sqlite') {
|
||||||
|
await repo.create({
|
||||||
|
values: {
|
||||||
|
id: 3,
|
||||||
|
createdAt: '2024-05-14 19:32:30.175 +00:00',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (dialect === 'postgres') {
|
||||||
|
await repo.create({
|
||||||
|
values: {
|
||||||
|
id: 3,
|
||||||
|
createdAt: '2024-05-14 19:32:30.175+00',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (dialect === 'mysql' || dialect === 'mariadb') {
|
||||||
|
await repo.create({
|
||||||
|
values: {
|
||||||
|
id: 3,
|
||||||
|
createdAt: '2024-05-14T19:32:30Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ctx = {
|
||||||
|
app,
|
||||||
|
db,
|
||||||
|
timezone: '+08:25',
|
||||||
|
action: {
|
||||||
|
params: {
|
||||||
|
values: {
|
||||||
|
collection: 'chart_test',
|
||||||
|
measures: [
|
||||||
|
{
|
||||||
|
field: ['id'],
|
||||||
|
aggregation: 'count',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
field: ['createdAt'],
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filter: {
|
||||||
|
id: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
await compose([parseFieldAndAssociations, parseBuilder, queryData])(ctx, async () => {});
|
||||||
|
expect(ctx.action.params.values.data).toBeDefined();
|
||||||
|
expect(ctx.action.params.values.data).toMatchObject([{ createdAt: '2024-05-15' }]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,8 +21,8 @@ describe('formatter', () => {
|
|||||||
}),
|
}),
|
||||||
col: (field: string) => field,
|
col: (field: string) => field,
|
||||||
getDialect: () => 'sqlite',
|
getDialect: () => 'sqlite',
|
||||||
};
|
} as any;
|
||||||
const result = formatter(sequelize, 'datetime', field, format);
|
const result = formatter(sequelize, 'datetime', field, format) as any;
|
||||||
expect(result.format).toEqual('%Y-%m-%d %H:%M:%S');
|
expect(result.format).toEqual('%Y-%m-%d %H:%M:%S');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -35,8 +35,8 @@ describe('formatter', () => {
|
|||||||
}),
|
}),
|
||||||
col: (field: string) => field,
|
col: (field: string) => field,
|
||||||
getDialect: () => 'mysql',
|
getDialect: () => 'mysql',
|
||||||
};
|
} as any;
|
||||||
const result = formatter(sequelize, 'datetime', field, format);
|
const result = formatter(sequelize, 'datetime', field, format) as any;
|
||||||
expect(result.format).toEqual('%Y-%m-%d %H:%i:%S');
|
expect(result.format).toEqual('%Y-%m-%d %H:%i:%S');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,8 +49,8 @@ describe('formatter', () => {
|
|||||||
}),
|
}),
|
||||||
col: (field: string) => field,
|
col: (field: string) => field,
|
||||||
getDialect: () => 'postgres',
|
getDialect: () => 'postgres',
|
||||||
};
|
} as any;
|
||||||
const result = formatter(sequelize, 'datetime', field, format);
|
const result = formatter(sequelize, 'datetime', field, format) as any;
|
||||||
expect(result.format).toEqual('YYYY-MM-DD HH24:MI:SS');
|
expect(result.format).toEqual('YYYY-MM-DD HH24:MI:SS');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,26 +18,22 @@ import {
|
|||||||
parseVariables,
|
parseVariables,
|
||||||
postProcess,
|
postProcess,
|
||||||
} from '../actions/query';
|
} from '../actions/query';
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
|
||||||
const formatter = await import('../actions/formatter');
|
const formatter = await import('../actions/formatter');
|
||||||
|
|
||||||
describe('query', () => {
|
describe('query', () => {
|
||||||
describe('parseBuilder', () => {
|
describe('parseBuilder', () => {
|
||||||
const sequelize = {
|
|
||||||
fn: vi.fn().mockImplementation((fn: string, field: string) => [fn, field]),
|
|
||||||
col: vi.fn().mockImplementation((field: string) => field),
|
|
||||||
getDialect() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let ctx: any;
|
let ctx: any;
|
||||||
let app: MockServer;
|
let app: MockServer;
|
||||||
|
let db: Database;
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await createMockServer({
|
app = await createMockServer({
|
||||||
plugins: ['data-source-manager', 'users', 'acl'],
|
plugins: ['data-source-manager', 'users', 'acl'],
|
||||||
});
|
});
|
||||||
app.db.options.underscored = true;
|
db = app.db;
|
||||||
app.db.collection({
|
db.options.underscored = true;
|
||||||
|
db.collection({
|
||||||
name: 'orders',
|
name: 'orders',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
@ -63,9 +59,8 @@ describe('query', () => {
|
|||||||
});
|
});
|
||||||
ctx = {
|
ctx = {
|
||||||
app,
|
app,
|
||||||
db: app.db,
|
db,
|
||||||
};
|
};
|
||||||
ctx.db.sequelize = sequelize;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check permissions', async () => {
|
it('should check permissions', async () => {
|
||||||
@ -141,6 +136,7 @@ describe('query', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse measures', async () => {
|
it('should parse measures', async () => {
|
||||||
const measures1 = [
|
const measures1 = [
|
||||||
{
|
{
|
||||||
@ -159,7 +155,9 @@ describe('query', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
await compose([parseFieldAndAssociations, parseBuilder])(context, async () => {});
|
await compose([parseFieldAndAssociations, parseBuilder])(context, async () => {});
|
||||||
expect(context.action.params.values.queryParams.attributes).toEqual([['orders.price', 'price']]);
|
expect(context.action.params.values.queryParams.attributes).toEqual([
|
||||||
|
[db.sequelize.col('orders.price'), 'price'],
|
||||||
|
]);
|
||||||
const measures2 = [
|
const measures2 = [
|
||||||
{
|
{
|
||||||
field: ['price'],
|
field: ['price'],
|
||||||
@ -179,8 +177,11 @@ describe('query', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
await compose([parseFieldAndAssociations, parseBuilder])(context2, async () => {});
|
await compose([parseFieldAndAssociations, parseBuilder])(context2, async () => {});
|
||||||
expect(context2.action.params.values.queryParams.attributes).toEqual([[['sum', 'orders.price'], 'price-alias']]);
|
expect(context2.action.params.values.queryParams.attributes).toEqual([
|
||||||
|
[db.sequelize.fn('sum', db.sequelize.col('orders.price')), 'price-alias'],
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse dimensions', async () => {
|
it('should parse dimensions', async () => {
|
||||||
vi.spyOn(formatter, 'formatter').mockReturnValue('formatted-field');
|
vi.spyOn(formatter, 'formatter').mockReturnValue('formatted-field');
|
||||||
const dimensions = [
|
const dimensions = [
|
||||||
@ -225,6 +226,7 @@ describe('query', () => {
|
|||||||
await compose([parseFieldAndAssociations, parseBuilder])(context2, async () => {});
|
await compose([parseFieldAndAssociations, parseBuilder])(context2, async () => {});
|
||||||
expect(context2.action.params.values.queryParams.group).toEqual(['formatted-field']);
|
expect(context2.action.params.values.queryParams.group).toEqual(['formatted-field']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse filter', async () => {
|
it('should parse filter', async () => {
|
||||||
const filter = {
|
const filter = {
|
||||||
createdAt: {
|
createdAt: {
|
||||||
|
@ -6,8 +6,25 @@
|
|||||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
import { Sequelize } from 'sequelize';
|
||||||
|
|
||||||
export const dateFormatFn = (sequelize: any, dialect: string, field: string, format: string) => {
|
const getOffsetMinutesFromTimezone = (timezone: string) => {
|
||||||
|
const sign = timezone.charAt(0);
|
||||||
|
timezone = timezone.slice(1);
|
||||||
|
const [hours, minutes] = timezone.split(':');
|
||||||
|
const hoursNum = Number(hours);
|
||||||
|
const minutesNum = Number(minutes);
|
||||||
|
const offset = hoursNum * 60 + minutesNum;
|
||||||
|
return `${sign}${offset} minutes`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dateFormatFn = (
|
||||||
|
sequelize: Sequelize,
|
||||||
|
dialect: string,
|
||||||
|
field: string,
|
||||||
|
format: string,
|
||||||
|
timezone?: string,
|
||||||
|
) => {
|
||||||
switch (dialect) {
|
switch (dialect) {
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
format = format
|
format = format
|
||||||
@ -17,6 +34,9 @@ export const dateFormatFn = (sequelize: any, dialect: string, field: string, for
|
|||||||
.replace(/hh/g, '%H')
|
.replace(/hh/g, '%H')
|
||||||
.replace(/mm/g, '%M')
|
.replace(/mm/g, '%M')
|
||||||
.replace(/ss/g, '%S');
|
.replace(/ss/g, '%S');
|
||||||
|
if (timezone) {
|
||||||
|
return sequelize.fn('strftime', format, sequelize.col(field), getOffsetMinutesFromTimezone(timezone));
|
||||||
|
}
|
||||||
return sequelize.fn('strftime', format, sequelize.col(field));
|
return sequelize.fn('strftime', format, sequelize.col(field));
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'mariadb':
|
case 'mariadb':
|
||||||
@ -27,9 +47,24 @@ export const dateFormatFn = (sequelize: any, dialect: string, field: string, for
|
|||||||
.replace(/hh/g, '%H')
|
.replace(/hh/g, '%H')
|
||||||
.replace(/mm/g, '%i')
|
.replace(/mm/g, '%i')
|
||||||
.replace(/ss/g, '%S');
|
.replace(/ss/g, '%S');
|
||||||
|
if (timezone) {
|
||||||
|
return sequelize.fn(
|
||||||
|
'date_format',
|
||||||
|
sequelize.fn('convert_tz', sequelize.col(field), process.env.DB_TIMEZONE || '+00:00', timezone),
|
||||||
|
format,
|
||||||
|
);
|
||||||
|
}
|
||||||
return sequelize.fn('date_format', sequelize.col(field), format);
|
return sequelize.fn('date_format', sequelize.col(field), format);
|
||||||
case 'postgres':
|
case 'postgres':
|
||||||
format = format.replace(/hh/g, 'HH24').replace(/mm/g, 'MI').replace(/ss/g, 'SS');
|
format = format.replace(/hh/g, 'HH24').replace(/mm/g, 'MI').replace(/ss/g, 'SS');
|
||||||
|
if (timezone) {
|
||||||
|
const fieldWithTZ = sequelize.literal(
|
||||||
|
`(${sequelize
|
||||||
|
.getQueryInterface()
|
||||||
|
.quoteIdentifiers(field)} AT TIME ZONE CURRENT_SETTING('TIMEZONE') AT TIME ZONE '${timezone}')`,
|
||||||
|
);
|
||||||
|
return sequelize.fn('to_char', fieldWithTZ, format);
|
||||||
|
}
|
||||||
return sequelize.fn('to_char', sequelize.col(field), format);
|
return sequelize.fn('to_char', sequelize.col(field), format);
|
||||||
default:
|
default:
|
||||||
return sequelize.col(field);
|
return sequelize.col(field);
|
||||||
@ -37,7 +72,7 @@ export const dateFormatFn = (sequelize: any, dialect: string, field: string, for
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* istanbul ignore next -- @preserve */
|
/* istanbul ignore next -- @preserve */
|
||||||
export const formatFn = (sequelize: any, dialect: string, field: string, format: string) => {
|
export const formatFn = (sequelize: Sequelize, dialect: string, field: string, format: string) => {
|
||||||
switch (dialect) {
|
switch (dialect) {
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
case 'postgres':
|
case 'postgres':
|
||||||
@ -47,13 +82,13 @@ export const formatFn = (sequelize: any, dialect: string, field: string, format:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatter = (sequelize: any, type: string, field: string, format: string) => {
|
export const formatter = (sequelize: Sequelize, type: string, field: string, format: string, timezone?: string) => {
|
||||||
const dialect = sequelize.getDialect();
|
const dialect = sequelize.getDialect();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'date':
|
case 'date':
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
case 'time':
|
case 'time':
|
||||||
return dateFormatFn(sequelize, dialect, field, format);
|
return dateFormatFn(sequelize, dialect, field, format, timezone);
|
||||||
default:
|
default:
|
||||||
return formatFn(sequelize, dialect, field, format);
|
return formatFn(sequelize, dialect, field, format);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ export const parseBuilder = async (ctx: Context, next: Next) => {
|
|||||||
const attribute = [];
|
const attribute = [];
|
||||||
const col = sequelize.col(field);
|
const col = sequelize.col(field);
|
||||||
if (format) {
|
if (format) {
|
||||||
attribute.push(formatter(sequelize, type, field, format));
|
attribute.push(formatter(sequelize, type, field, format, ctx.timezone));
|
||||||
} else {
|
} else {
|
||||||
attribute.push(col);
|
attribute.push(col);
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,32 @@ describe('multiple apps', () => {
|
|||||||
expect(subAppStatus).toEqual('running');
|
expect(subAppStatus).toEqual('running');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not create application named with main', async () => {
|
||||||
|
const name = 'main';
|
||||||
|
|
||||||
|
let err;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.getRepository('applications').create({
|
||||||
|
values: {
|
||||||
|
name,
|
||||||
|
options: {
|
||||||
|
plugins: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
waitSubAppInstall: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(err).toBeDefined();
|
||||||
|
|
||||||
|
expect(await db.getRepository('applications').count()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should upgrade sub app', async () => {
|
it('should upgrade sub app', async () => {
|
||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
|
@ -170,6 +170,12 @@ export class PluginMultiAppManagerServer extends Plugin {
|
|||||||
async (model: ApplicationModel, options: Transactionable & { context?: any }) => {
|
async (model: ApplicationModel, options: Transactionable & { context?: any }) => {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
|
const name = model.get('name') as string;
|
||||||
|
|
||||||
|
if (name === 'main') {
|
||||||
|
throw new Error('Application name "main" is reserved');
|
||||||
|
}
|
||||||
|
|
||||||
const subApp = model.registerToSupervisor(this.app, {
|
const subApp = model.registerToSupervisor(this.app, {
|
||||||
appOptionsFactory: this.appOptionsFactory,
|
appOptionsFactory: this.appOptionsFactory,
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user