* feat(db): add sql collection * feat: frontend * perf: issue of select * fix: sql model * fix: sql collection schema * fix: implement sql collection * fix: dependency * fix: remove type declaration in actions * fix: backend test * chore: remove some ops of block using sql collection * chore: remove sql collections from Form and Kanban * feat: add execute button to sql input * feat(backend): support infer fields by parsing sql * feat(frontend): support infer interface by parsing sql * fix: fix update issues and improve * fix: update issue * chore: update yarn.lock * fix: fix T-1548 * fix: fix T-1544 * fix: fix T-1545 * fix: fix T-1549 * fix: test * fix: fix T-1556 * fix: remove map action diviver * chore: debug * chore: remove schema of sql collection * fix: sql collection schema * chore: remove debug log & fix T-1555 * fix: fix T-1679 * fix: sql update issue * fix: sql attribute issue * fix: bug of star attribute * fix: test * fix: test * fix: reset fields when updating sql collection * fix(collection-manager): redundant fields after set collection fields * fix: test * fix: destory with individuals hook * chore: save * chore: test * fix: fields sync issue * fix: remove underscored option of sql collection * chore: mutex in fields.afterDestroy * fix: test * chore: yarn.lock * chore: update collections.setFields * feat: improve sql input * fix: fix T-1742 & improve * chore: fix conflicts * fix: workspace * fix: build * fix: test * chore: add translations * fix: reviewed issues * chore: update yarn.lock --------- Co-authored-by: ChengLei Shao <chareice@live.com>
97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import { Context, Next } from '@nocobase/actions';
|
|
import { SQLModel, SqlCollection } from '@nocobase/database';
|
|
import { CollectionModel } from '../models';
|
|
|
|
const updateCollection = async (ctx: Context, transaction: any) => {
|
|
const { filterByTk, values } = ctx.action.params;
|
|
const repo = ctx.db.getRepository('collections');
|
|
const collection: CollectionModel = await repo.findOne({
|
|
filter: {
|
|
name: filterByTk,
|
|
},
|
|
transaction,
|
|
});
|
|
const existFields = await collection.getFields({ transaction });
|
|
const deletedFields = existFields.filter((field: any) => !values.fields?.find((f: any) => f.name === field.name));
|
|
for (const field of deletedFields) {
|
|
await field.destroy({ transaction });
|
|
}
|
|
const upRes = await repo.update({
|
|
filterByTk,
|
|
values,
|
|
updateAssociationValues: ['fields'],
|
|
transaction,
|
|
});
|
|
|
|
return { collection, upRes };
|
|
};
|
|
|
|
export default {
|
|
name: 'sqlCollection',
|
|
actions: {
|
|
execute: async (ctx: Context, next: Next) => {
|
|
let {
|
|
values: { sql },
|
|
} = ctx.action.params;
|
|
sql = sql.trim().split(';').shift();
|
|
if (!sql) {
|
|
ctx.throw(400, ctx.t('SQL is empty'));
|
|
}
|
|
if (!/^select/i.test(sql) && !/^with([\s\S]+)select([\s\S]+)/i.test(sql)) {
|
|
ctx.throw(400, ctx.t('Only select query allowed'));
|
|
}
|
|
const tmpCollection = new SqlCollection({ name: 'tmp', sql }, { database: ctx.db });
|
|
const model = tmpCollection.model as typeof SQLModel;
|
|
// The result is for preview only, add limit clause to avoid too many results
|
|
const data = await model.findAll({ attributes: ['*'], limit: 5, raw: true });
|
|
let fields: {
|
|
[field: string]: {
|
|
type: string;
|
|
source: string;
|
|
collection: string;
|
|
interface: string;
|
|
};
|
|
} = {};
|
|
try {
|
|
fields = model.inferFields();
|
|
} catch (err) {
|
|
ctx.logger.warn('resource: sql-collection, action: execute, error: ', err);
|
|
fields = {};
|
|
}
|
|
const sources = Array.from(new Set(Object.values(fields).map((field) => field.collection)));
|
|
ctx.body = { data, fields, sources };
|
|
await next();
|
|
},
|
|
setFields: async (ctx: Context, next: Next) => {
|
|
const transaction = await ctx.app.db.sequelize.transaction();
|
|
try {
|
|
const {
|
|
upRes: [collection],
|
|
} = await updateCollection(ctx, transaction);
|
|
await collection.loadFields({
|
|
transaction,
|
|
});
|
|
await transaction.commit();
|
|
} catch (e) {
|
|
await transaction.rollback();
|
|
throw e;
|
|
}
|
|
await next();
|
|
},
|
|
update: async (ctx: Context, next: Next) => {
|
|
const transaction = await ctx.app.db.sequelize.transaction();
|
|
try {
|
|
const { upRes } = await updateCollection(ctx, transaction);
|
|
const [collection] = upRes;
|
|
await (collection as CollectionModel).load({ transaction, resetFields: true });
|
|
await transaction.commit();
|
|
ctx.body = upRes;
|
|
} catch (e) {
|
|
await transaction.rollback();
|
|
throw e;
|
|
}
|
|
await next();
|
|
},
|
|
},
|
|
};
|