test: with collection_manager_schema env (#1532)
* test: with collection_manager_schema env * fix: remove collection * fix: collection test * fix: collection exist in db with custom schema * fix: inherited with custom collection schema * fix: build error * fix: sync unique index & database logger
This commit is contained in:
parent
104be20c60
commit
d1fb3c92d8
2
.github/workflows/nocobase-test.yml
vendored
2
.github/workflows/nocobase-test.yml
vendored
@ -56,6 +56,7 @@ jobs:
|
|||||||
node_version: ['18']
|
node_version: ['18']
|
||||||
underscored: [true, false]
|
underscored: [true, false]
|
||||||
schema: [public, nocobase]
|
schema: [public, nocobase]
|
||||||
|
collection_schema: [public, user_schema]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: node:${{ matrix.node_version }}
|
container: node:${{ matrix.node_version }}
|
||||||
services:
|
services:
|
||||||
@ -93,6 +94,7 @@ jobs:
|
|||||||
DB_DATABASE: nocobase
|
DB_DATABASE: nocobase
|
||||||
DB_UNDERSCORED: ${{ matrix.underscored }}
|
DB_UNDERSCORED: ${{ matrix.underscored }}
|
||||||
DB_SCHEMA: ${{ matrix.schema }}
|
DB_SCHEMA: ${{ matrix.schema }}
|
||||||
|
COLLECTION_MANAGER_SCHEMA: ${{ matrix.collection_schema }}
|
||||||
|
|
||||||
mysql-test:
|
mysql-test:
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -44,5 +44,9 @@
|
|||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"pretty-format": "^24.0.0",
|
"pretty-format": "^24.0.0",
|
||||||
"pretty-quick": "^3.1.0"
|
"pretty-quick": "^3.1.0"
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "18.14.2",
|
||||||
|
"yarn": "1.22.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nocobase/utils": "0.9.1-alpha.1",
|
"@nocobase/utils": "0.9.1-alpha.1",
|
||||||
|
"@nocobase/logger": "0.9.1-alpha.1",
|
||||||
"async-mutex": "^0.3.2",
|
"async-mutex": "^0.3.2",
|
||||||
"cron-parser": "4.4.0",
|
"cron-parser": "4.4.0",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
|
@ -308,13 +308,13 @@ export class Collection<
|
|||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
const queryInterface = this.db.sequelize.getQueryInterface();
|
const queryInterface = this.db.sequelize.getQueryInterface();
|
||||||
await queryInterface.dropTable(this.model.tableName, options);
|
await queryInterface.dropTable(this.addSchemaTableName(), options);
|
||||||
}
|
}
|
||||||
this.remove();
|
this.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
async existsInDb(options?: Transactionable) {
|
async existsInDb(options?: Transactionable) {
|
||||||
return this.db.collectionExistsInDb(this.name, options);
|
return this.db.queryInterface.collectionTableExists(this, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeField(name: string): void | Field {
|
removeField(name: string): void | Field {
|
||||||
@ -562,6 +562,10 @@ export class Collection<
|
|||||||
return this.db.options.schema;
|
return this.db.options.schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.db.inDialect('postgres')) {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
Sequelize,
|
Sequelize,
|
||||||
SyncOptions,
|
SyncOptions,
|
||||||
Transactionable,
|
Transactionable,
|
||||||
Utils
|
Utils,
|
||||||
} from 'sequelize';
|
} from 'sequelize';
|
||||||
import { SequelizeStorage, Umzug } from 'umzug';
|
import { SequelizeStorage, Umzug } from 'umzug';
|
||||||
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
||||||
@ -58,12 +58,15 @@ import {
|
|||||||
SyncListener,
|
SyncListener,
|
||||||
UpdateListener,
|
UpdateListener,
|
||||||
UpdateWithAssociationsListener,
|
UpdateWithAssociationsListener,
|
||||||
ValidateListener
|
ValidateListener,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
||||||
|
|
||||||
import DatabaseUtils from './database-utils';
|
import DatabaseUtils from './database-utils';
|
||||||
import { BaseValueParser, registerFieldValueParsers } from './value-parsers';
|
import { BaseValueParser, registerFieldValueParsers } from './value-parsers';
|
||||||
|
import buildQueryInterface from './query-interface/query-interface-builder';
|
||||||
|
import QueryInterface from './query-interface/query-interface';
|
||||||
|
import { Logger } from '@nocobase/logger';
|
||||||
|
|
||||||
export interface MergeOptions extends merge.Options {}
|
export interface MergeOptions extends merge.Options {}
|
||||||
|
|
||||||
@ -166,6 +169,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
modelCollection = new Map<ModelStatic<any>, Collection>();
|
modelCollection = new Map<ModelStatic<any>, Collection>();
|
||||||
tableNameCollectionMap = new Map<string, Collection>();
|
tableNameCollectionMap = new Map<string, Collection>();
|
||||||
|
|
||||||
|
queryInterface: QueryInterface;
|
||||||
|
|
||||||
utils = new DatabaseUtils(this);
|
utils = new DatabaseUtils(this);
|
||||||
referenceMap = new ReferencesMap();
|
referenceMap = new ReferencesMap();
|
||||||
inheritanceMap = new InheritanceMap();
|
inheritanceMap = new InheritanceMap();
|
||||||
@ -177,6 +182,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
|
|
||||||
delayCollectionExtend = new Map<string, { collectionOptions: CollectionOptions; mergeOptions?: any }[]>();
|
delayCollectionExtend = new Map<string, { collectionOptions: CollectionOptions; mergeOptions?: any }[]>();
|
||||||
|
|
||||||
|
logger: Logger;
|
||||||
|
|
||||||
constructor(options: DatabaseOptions) {
|
constructor(options: DatabaseOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -212,6 +219,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
|
|
||||||
this.sequelize = new Sequelize(opts);
|
this.sequelize = new Sequelize(opts);
|
||||||
|
|
||||||
|
this.queryInterface = buildQueryInterface(this);
|
||||||
|
|
||||||
this.collections = new Map();
|
this.collections = new Map();
|
||||||
this.modelHook = new ModelHook(this);
|
this.modelHook = new ModelHook(this);
|
||||||
|
|
||||||
@ -280,6 +289,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
patchSequelizeQueryInterface(this);
|
patchSequelizeQueryInterface(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLogger(logger: Logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
initListener() {
|
initListener() {
|
||||||
this.on('beforeDefine', (model, options) => {
|
this.on('beforeDefine', (model, options) => {
|
||||||
if (this.options.underscored) {
|
if (this.options.underscored) {
|
||||||
@ -631,15 +644,12 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
|
|
||||||
async collectionExistsInDb(name: string, options?: Transactionable) {
|
async collectionExistsInDb(name: string, options?: Transactionable) {
|
||||||
const collection = this.getCollection(name);
|
const collection = this.getCollection(name);
|
||||||
|
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tables = await this.sequelize.getQueryInterface().showAllTables({
|
return await this.queryInterface.collectionTableExists(collection, options);
|
||||||
transaction: options?.transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
return tables.includes(this.getCollection(name).model.tableName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isSqliteMemory() {
|
public isSqliteMemory() {
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import QueryInterface from './query-interface';
|
||||||
|
import { Collection } from '../collection';
|
||||||
|
import { Transactionable } from 'sequelize';
|
||||||
|
|
||||||
|
export default class MysqlQueryInterface extends QueryInterface {
|
||||||
|
constructor(db) {
|
||||||
|
super(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
async collectionTableExists(collection: Collection, options?: Transactionable) {
|
||||||
|
const transaction = options?.transaction;
|
||||||
|
|
||||||
|
const tableName = collection.model.tableName;
|
||||||
|
const databaseName = this.db.options.database;
|
||||||
|
const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${databaseName}' AND TABLE_NAME = '${tableName}'`;
|
||||||
|
|
||||||
|
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
||||||
|
return results.length > 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import QueryInterface from './query-interface';
|
||||||
|
import { Collection } from '../collection';
|
||||||
|
|
||||||
|
export default class PostgresQueryInterface extends QueryInterface {
|
||||||
|
constructor(db) {
|
||||||
|
super(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
async collectionTableExists(collection: Collection, options?) {
|
||||||
|
const transaction = options?.transaction;
|
||||||
|
|
||||||
|
const tableName = collection.model.tableName;
|
||||||
|
const schema = collection.collectionSchema() || 'public';
|
||||||
|
|
||||||
|
const sql = `SELECT EXISTS(SELECT 1 FROM information_schema.tables
|
||||||
|
WHERE table_schema = '${schema}'
|
||||||
|
AND table_name = '${tableName}')`;
|
||||||
|
|
||||||
|
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
||||||
|
return results[0]['exists'];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import Database from '../database';
|
||||||
|
import MysqlQueryInterface from './mysql-query-interface';
|
||||||
|
import PostgresQueryInterface from './postgres-query-interface';
|
||||||
|
import SqliteQueryInterface from './sqlite-query-interface';
|
||||||
|
|
||||||
|
export default function buildQueryInterface(db: Database) {
|
||||||
|
const map = {
|
||||||
|
mysql: MysqlQueryInterface,
|
||||||
|
postgres: PostgresQueryInterface,
|
||||||
|
sqlite: SqliteQueryInterface,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new map[db.options.dialect](db);
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import Database from '../database';
|
||||||
|
import { Collection } from '../collection';
|
||||||
|
import { QueryInterface as SequelizeQueryInterface, Transactionable } from 'sequelize';
|
||||||
|
|
||||||
|
export default abstract class QueryInterface {
|
||||||
|
sequelizeQueryInterface: SequelizeQueryInterface;
|
||||||
|
protected constructor(public db: Database) {
|
||||||
|
this.sequelizeQueryInterface = db.sequelize.getQueryInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract collectionTableExists(collection: Collection, options?: Transactionable): Promise<boolean>;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import QueryInterface from './query-interface';
|
||||||
|
import { Collection } from '../collection';
|
||||||
|
|
||||||
|
export default class SqliteQueryInterface extends QueryInterface {
|
||||||
|
constructor(db) {
|
||||||
|
super(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
async collectionTableExists(collection: Collection, options?) {
|
||||||
|
const transaction = options?.transaction;
|
||||||
|
|
||||||
|
const tableName = collection.model.tableName;
|
||||||
|
|
||||||
|
const sql = `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}';`;
|
||||||
|
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
||||||
|
return results.length > 0;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@ export class SyncRunner {
|
|||||||
|
|
||||||
const inheritedCollection = model.collection as InheritedCollection;
|
const inheritedCollection = model.collection as InheritedCollection;
|
||||||
const db = inheritedCollection.context.database;
|
const db = inheritedCollection.context.database;
|
||||||
const schemaName = db.options.schema || 'public';
|
|
||||||
|
|
||||||
const dialect = db.sequelize.getDialect();
|
const dialect = db.sequelize.getDialect();
|
||||||
|
|
||||||
@ -27,12 +26,7 @@ export class SyncRunner {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentTables = parents.map((parent) => parent.model.tableName);
|
const tableName = inheritedCollection.addSchemaTableName();
|
||||||
|
|
||||||
const tableName = model.tableName;
|
|
||||||
|
|
||||||
const schemaTableName = db.utils.addSchema(tableName);
|
|
||||||
const quoteTableName = db.utils.quoteTable(tableName);
|
|
||||||
|
|
||||||
const attributes = model.tableAttributes;
|
const attributes = model.tableAttributes;
|
||||||
|
|
||||||
@ -43,13 +37,14 @@ export class SyncRunner {
|
|||||||
let maxSequenceVal = 0;
|
let maxSequenceVal = 0;
|
||||||
let maxSequenceName;
|
let maxSequenceName;
|
||||||
|
|
||||||
|
// find max sequence
|
||||||
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
||||||
for (const parent of parentTables) {
|
for (const parent of parents) {
|
||||||
const sequenceNameResult = await queryInterface.sequelize.query(
|
const sequenceNameResult = await queryInterface.sequelize.query(
|
||||||
`SELECT column_default
|
`SELECT column_default
|
||||||
FROM information_schema.columns
|
FROM information_schema.columns
|
||||||
WHERE table_name = '${parent}'
|
WHERE table_name = '${parent.model.tableName}'
|
||||||
and table_schema = '${schemaName}'
|
and table_schema = '${parent.collectionSchema()}'
|
||||||
and "column_name" = 'id';`,
|
and "column_name" = 'id';`,
|
||||||
{
|
{
|
||||||
transaction,
|
transaction,
|
||||||
@ -88,22 +83,24 @@ export class SyncRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createTable(schemaTableName, childAttributes, options, model, parentTables, db);
|
await this.createTable(tableName, childAttributes, options, model, parents);
|
||||||
|
|
||||||
|
// if we have max sequence, set it to child table
|
||||||
if (maxSequenceName) {
|
if (maxSequenceName) {
|
||||||
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(
|
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map((parent) =>
|
||||||
(parent) => db.getCollection(parent).model.tableName,
|
db.getCollection(parent).addSchemaTableName(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const sequenceTables = [...parentsDeep, tableName.toString()];
|
const sequenceTables = [...parentsDeep, tableName];
|
||||||
|
|
||||||
for (const sequenceTable of sequenceTables) {
|
for (const sequenceTable of sequenceTables) {
|
||||||
const queryName =
|
const tableName = sequenceTable.tableName;
|
||||||
Boolean(sequenceTable.match(/[A-Z]/)) && !sequenceTable.includes(`"`) ? `"${sequenceTable}"` : sequenceTable;
|
const schemaName = sequenceTable.schema;
|
||||||
|
|
||||||
|
const queryName = Boolean(tableName.match(/[A-Z]/)) && !tableName.includes(`"`) ? `"${tableName}"` : tableName;
|
||||||
|
|
||||||
const idColumnQuery = await queryInterface.sequelize.query(
|
const idColumnQuery = await queryInterface.sequelize.query(
|
||||||
`
|
`SELECT column_name
|
||||||
SELECT column_name
|
|
||||||
FROM information_schema.columns
|
FROM information_schema.columns
|
||||||
WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
|
WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
|
||||||
`,
|
`,
|
||||||
@ -117,7 +114,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|||||||
}
|
}
|
||||||
|
|
||||||
await queryInterface.sequelize.query(
|
await queryInterface.sequelize.query(
|
||||||
`alter table "${schemaName}"."${sequenceTable}"
|
`alter table ${db.utils.quoteTable(sequenceTable)}
|
||||||
alter column id set default nextval('${maxSequenceName}')`,
|
alter column id set default nextval('${maxSequenceName}')`,
|
||||||
{
|
{
|
||||||
transaction,
|
transaction,
|
||||||
@ -139,7 +136,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createTable(tableName, attributes, options, model, parentTables, db) {
|
static async createTable(tableName, attributes, options, model, parents) {
|
||||||
let sql = '';
|
let sql = '';
|
||||||
|
|
||||||
options = { ...options };
|
options = { ...options };
|
||||||
@ -164,9 +161,9 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|||||||
|
|
||||||
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
|
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
|
||||||
';',
|
';',
|
||||||
` INHERITS (${parentTables
|
` INHERITS (${parents
|
||||||
.map((t) => {
|
.map((t) => {
|
||||||
return db.utils.quoteTable(db.utils.addSchema(t, db.options.schema));
|
return t.addSchemaTableName();
|
||||||
})
|
})
|
||||||
.join(', ')});`,
|
.join(', ')});`,
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import crypto from 'crypto';
|
|||||||
import Database from './database';
|
import Database from './database';
|
||||||
import { IdentifierError } from './errors/identifier-error';
|
import { IdentifierError } from './errors/identifier-error';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
|
import lodash from 'lodash';
|
||||||
|
|
||||||
type HandleAppendsQueryOptions = {
|
type HandleAppendsQueryOptions = {
|
||||||
templateModel: any;
|
templateModel: any;
|
||||||
@ -96,15 +97,15 @@ function patchShowConstraintsQuery(queryGenerator, db) {
|
|||||||
'is_deferrable AS "isDeferrable",',
|
'is_deferrable AS "isDeferrable",',
|
||||||
'initially_deferred AS "initiallyDeferred"',
|
'initially_deferred AS "initiallyDeferred"',
|
||||||
'from INFORMATION_SCHEMA.table_constraints',
|
'from INFORMATION_SCHEMA.table_constraints',
|
||||||
`WHERE table_name='${tableName}'`,
|
`WHERE table_name='${lodash.isPlainObject(tableName) ? tableName.tableName : tableName}'`,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!constraintName) {
|
if (!constraintName) {
|
||||||
lines.push(`AND constraint_name='${constraintName}'`);
|
lines.push(`AND constraint_name='${constraintName}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (db.options.schema && db.options.schema !== 'public') {
|
if (lodash.isPlainObject(tableName) && tableName.schema) {
|
||||||
lines.push(`AND table_schema='${db.options.schema}'`);
|
lines.push(`AND table_schema='${tableName.schema}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join(' ');
|
return lines.join(' ');
|
||||||
|
@ -283,12 +283,15 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createDatabase(options: ApplicationOptions) {
|
private createDatabase(options: ApplicationOptions) {
|
||||||
return new Database({
|
const db = new Database({
|
||||||
...(options.database instanceof Database ? options.database.options : options.database),
|
...(options.database instanceof Database ? options.database.options : options.database),
|
||||||
migrator: {
|
migrator: {
|
||||||
context: { app: this },
|
context: { app: this },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
db.setLogger(this._logger);
|
||||||
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
|
@ -249,7 +249,7 @@ describe('collections repository', () => {
|
|||||||
|
|
||||||
const testCollection = db.getCollection('tests');
|
const testCollection = db.getCollection('tests');
|
||||||
const getTableInfo = async () =>
|
const getTableInfo = async () =>
|
||||||
await db.sequelize.getQueryInterface().describeTable(testCollection.model.tableName);
|
await db.sequelize.getQueryInterface().describeTable(testCollection.addSchemaTableName());
|
||||||
|
|
||||||
const tableInfo0 = await getTableInfo();
|
const tableInfo0 = await getTableInfo();
|
||||||
expect(tableInfo0['date_a']).toBeDefined();
|
expect(tableInfo0['date_a']).toBeDefined();
|
||||||
@ -286,7 +286,7 @@ describe('collections repository', () => {
|
|||||||
|
|
||||||
const testCollection = db.getCollection('tests');
|
const testCollection = db.getCollection('tests');
|
||||||
const getTableInfo = async () =>
|
const getTableInfo = async () =>
|
||||||
await db.sequelize.getQueryInterface().describeTable(testCollection.model.tableName);
|
await db.sequelize.getQueryInterface().describeTable(testCollection.addSchemaTableName());
|
||||||
|
|
||||||
const tableInfo0 = await getTableInfo();
|
const tableInfo0 = await getTableInfo();
|
||||||
expect(tableInfo0[createdAt]).toBeDefined();
|
expect(tableInfo0[createdAt]).toBeDefined();
|
||||||
@ -339,7 +339,7 @@ describe('collections repository', () => {
|
|||||||
testCollection.model.rawAttributes.test_field.field === testCollection.model.rawAttributes.testField.field,
|
testCollection.model.rawAttributes.test_field.field === testCollection.model.rawAttributes.testField.field,
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
const getTableInfo = async () =>
|
const getTableInfo = async () =>
|
||||||
await db.sequelize.getQueryInterface().describeTable(testCollection.model.tableName);
|
await db.sequelize.getQueryInterface().describeTable(testCollection.addSchemaTableName());
|
||||||
|
|
||||||
const tableInfo0 = await getTableInfo();
|
const tableInfo0 = await getTableInfo();
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ describe('field defaultValue', () => {
|
|||||||
const response2 = await app.agent().resource('test1').create();
|
const response2 = await app.agent().resource('test1').create();
|
||||||
expect(response2.body.data.field1).toBe('cba');
|
expect(response2.body.data.field1).toBe('cba');
|
||||||
|
|
||||||
const results = await app.db.sequelize.getQueryInterface().describeTable(TestCollection.model.tableName);
|
const results = await app.db.sequelize.getQueryInterface().describeTable(TestCollection.addSchemaTableName());
|
||||||
|
|
||||||
expect(results.field1.defaultValue).toBe('cba');
|
expect(results.field1.defaultValue).toBe('cba');
|
||||||
});
|
});
|
||||||
|
@ -460,7 +460,7 @@ describe('collections repository', () => {
|
|||||||
|
|
||||||
const columnName = collection.model.rawAttributes.testField.field;
|
const columnName = collection.model.rawAttributes.testField.field;
|
||||||
|
|
||||||
const tableInfo = await app.db.sequelize.getQueryInterface().describeTable(collection.model.tableName);
|
const tableInfo = await app.db.sequelize.getQueryInterface().describeTable(collection.addSchemaTableName());
|
||||||
|
|
||||||
expect(tableInfo[columnName]).toBeDefined();
|
expect(tableInfo[columnName]).toBeDefined();
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ describe('collections', () => {
|
|||||||
await app.destroy();
|
await app.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('remove collection', async () => {
|
test('remove collection 1', async () => {
|
||||||
await app
|
await app
|
||||||
.agent()
|
.agent()
|
||||||
.resource('collections')
|
.resource('collections')
|
||||||
@ -23,16 +23,15 @@ describe('collections', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const collection = app.db.getCollection('test');
|
const collection = app.db.getCollection('test');
|
||||||
const r1 = await collection.existsInDb();
|
expect(await collection.existsInDb()).toBeTruthy();
|
||||||
expect(r1).toBe(true);
|
|
||||||
await app.agent().resource('collections').destroy({
|
await app.agent().resource('collections').destroy({
|
||||||
filterByTk: 'test',
|
filterByTk: 'test',
|
||||||
});
|
});
|
||||||
const r2 = await collection.existsInDb();
|
|
||||||
expect(r2).toBe(false);
|
expect(await collection.existsInDb()).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('remove collection', async () => {
|
test('remove collection 2', async () => {
|
||||||
await app
|
await app
|
||||||
.agent()
|
.agent()
|
||||||
.resource('collections')
|
.resource('collections')
|
||||||
@ -77,7 +76,7 @@ describe('collections', () => {
|
|||||||
expect(count).toBe(0);
|
expect(count).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('remove collection', async () => {
|
test('remove collection 3', async () => {
|
||||||
await app
|
await app
|
||||||
.agent()
|
.agent()
|
||||||
.resource('collections')
|
.resource('collections')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Database, { Collection, MagicAttributeModel, snakeCase } from '@nocobase/database';
|
import Database, { Collection, MagicAttributeModel } from '@nocobase/database';
|
||||||
import { SyncOptions, Transactionable } from 'sequelize';
|
import { SyncOptions, Transactionable } from 'sequelize';
|
||||||
|
|
||||||
interface LoadOptions extends Transactionable {
|
interface LoadOptions extends Transactionable {
|
||||||
@ -120,7 +120,11 @@ export class FieldModel extends MagicAttributeModel {
|
|||||||
let constraintName = `${tableName}_${field.name}_uk`;
|
let constraintName = `${tableName}_${field.name}_uk`;
|
||||||
|
|
||||||
if (existUniqueIndex) {
|
if (existUniqueIndex) {
|
||||||
const existsUniqueConstraints = await queryInterface.showConstraint(tableName, constraintName, {});
|
const existsUniqueConstraints = await queryInterface.showConstraint(
|
||||||
|
collection.addSchemaTableName(),
|
||||||
|
constraintName,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
existsUniqueConstraint = existsUniqueConstraints[0];
|
existsUniqueConstraint = existsUniqueConstraints[0];
|
||||||
}
|
}
|
||||||
@ -135,12 +139,16 @@ export class FieldModel extends MagicAttributeModel {
|
|||||||
name: constraintName,
|
name: constraintName,
|
||||||
transaction: options.transaction,
|
transaction: options.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.db.logger.info(`add unique index ${constraintName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unique && existsUniqueConstraint) {
|
if (!unique && existsUniqueConstraint) {
|
||||||
await queryInterface.removeConstraint(collection.addSchemaTableName(), constraintName, {
|
await queryInterface.removeConstraint(collection.addSchemaTableName(), constraintName, {
|
||||||
transaction: options.transaction,
|
transaction: options.transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.db.logger.info(`remove unique index ${constraintName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user