@nocobase/plugin-data-source-main 2.1.0-beta.9 → 2.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var service_exports = {};
28
+ __export(service_exports, {
29
+ applyCollectionDefinition: () => applyCollectionDefinition,
30
+ applyFieldDefinition: () => applyFieldDefinition,
31
+ findCollection: () => findCollection,
32
+ pickCollectionApplyValues: () => pickCollectionApplyValues,
33
+ upsertFieldDefinition: () => upsertFieldDefinition
34
+ });
35
+ module.exports = __toCommonJS(service_exports);
36
+ var import_capabilities = require("./capabilities");
37
+ var import_collections = require("./collections");
38
+ var import_constants = require("./constants");
39
+ var import_fields = require("./fields");
40
+ function mergeSettings(input = {}) {
41
+ const { settings, ...rest } = input;
42
+ return {
43
+ ...settings || {},
44
+ ...rest
45
+ };
46
+ }
47
+ function validateApplyFieldInputs(fields) {
48
+ if (!Array.isArray(fields)) {
49
+ return;
50
+ }
51
+ fields.forEach((field) => (0, import_fields.validateChoiceFieldInput)(field));
52
+ }
53
+ async function findCollection(ctx, collectionName) {
54
+ return ctx.app.db.getRepository("collections").findOne({
55
+ filter: {
56
+ name: collectionName
57
+ },
58
+ appends: ["fields"]
59
+ });
60
+ }
61
+ function pickCollectionApplyValues(values) {
62
+ const normalized = { ...values };
63
+ delete normalized.fields;
64
+ delete normalized.name;
65
+ delete normalized.replaceFields;
66
+ delete normalized.verify;
67
+ delete normalized.settings;
68
+ return normalized;
69
+ }
70
+ async function upsertFieldDefinition(ctx, rawInput) {
71
+ const input = mergeSettings(rawInput);
72
+ const collectionName = input.collectionName;
73
+ if (!collectionName) {
74
+ throw new Error("field apply requires collectionName");
75
+ }
76
+ (0, import_fields.validateChoiceFieldInput)(input);
77
+ const values = (0, import_fields.normalizeFieldInput)(input, { collectionName });
78
+ await (0, import_capabilities.assertModelingSupport)(ctx.app, { fields: [values] });
79
+ const repository = ctx.app.db.getRepository("fields");
80
+ const existing = await repository.findOne({
81
+ filter: {
82
+ collectionName,
83
+ name: values.name
84
+ }
85
+ });
86
+ if (existing) {
87
+ await repository.update({
88
+ filterByTk: existing.key,
89
+ values,
90
+ context: ctx
91
+ });
92
+ } else {
93
+ await repository.create({
94
+ values,
95
+ context: ctx
96
+ });
97
+ }
98
+ return repository.findOne({
99
+ filter: {
100
+ collectionName,
101
+ name: values.name
102
+ }
103
+ });
104
+ }
105
+ async function applyFieldDefinition(ctx, rawInput, options = {}) {
106
+ const input = mergeSettings(rawInput);
107
+ if (options.relationOnly && !import_constants.RELATION_INTERFACES.has(input.interface)) {
108
+ throw new Error(`fields:apply relation mode requires relation interface, got ${input.interface || "empty"}`);
109
+ }
110
+ return upsertFieldDefinition(ctx, input);
111
+ }
112
+ async function applyCollectionDefinition(ctx, rawInput, syncCollectionFields) {
113
+ const input = mergeSettings(rawInput);
114
+ const collectionName = input.name || ctx.action.params.filterByTk;
115
+ const replaceFields = input.replaceFields === true;
116
+ const shouldVerify = input.verify !== false;
117
+ const repository = ctx.app.db.getRepository("collections");
118
+ if (!collectionName) {
119
+ throw new Error("collections:apply requires name");
120
+ }
121
+ validateApplyFieldInputs(input.fields);
122
+ const existing = await findCollection(ctx, collectionName);
123
+ if (!existing) {
124
+ const values = (0, import_collections.composeCollectionDefinition)({ ...input, name: collectionName }, { mode: "create" });
125
+ await (0, import_capabilities.assertModelingSupport)(ctx.app, values);
126
+ await repository.create({
127
+ values,
128
+ context: ctx
129
+ });
130
+ } else {
131
+ const values = (0, import_collections.normalizeCollectionInput)({ ...input, name: collectionName }, { mode: "update" });
132
+ await (0, import_capabilities.assertModelingSupport)(ctx.app, values);
133
+ const collectionValues = pickCollectionApplyValues(values);
134
+ if (Object.keys(collectionValues).length > 0) {
135
+ await repository.update({
136
+ filterByTk: collectionName,
137
+ values: collectionValues,
138
+ context: ctx
139
+ });
140
+ }
141
+ if (Array.isArray(values.fields) && values.fields.length > 0) {
142
+ if (replaceFields) {
143
+ await syncCollectionFields(ctx, collectionName, {
144
+ fields: (0, import_fields.normalizeFieldList)(values.fields, collectionName)
145
+ });
146
+ } else {
147
+ for (const field of values.fields) {
148
+ await upsertFieldDefinition(ctx, {
149
+ ...field,
150
+ collectionName
151
+ });
152
+ }
153
+ }
154
+ }
155
+ }
156
+ const collection = await findCollection(ctx, collectionName);
157
+ const verify = shouldVerify ? (0, import_capabilities.verifyCollectionDefinition)(ctx.app, collectionName) : void 0;
158
+ return {
159
+ data: collection,
160
+ verify
161
+ };
162
+ }
163
+ // Annotate the CommonJS export names for ESM import in node:
164
+ 0 && (module.exports = {
165
+ applyCollectionDefinition,
166
+ applyFieldDefinition,
167
+ findCollection,
168
+ pickCollectionApplyValues,
169
+ upsertFieldDefinition
170
+ });
@@ -41,6 +41,7 @@ __export(collection_exports, {
41
41
  module.exports = __toCommonJS(collection_exports);
42
42
  var import_database = require("@nocobase/database");
43
43
  var import_lodash = __toESM(require("lodash"));
44
+ const databaseSyncedCollectionSources = ["db2cm", "dbsync"];
44
45
  class CollectionModel extends import_database.MagicAttributeModel {
45
46
  get db() {
46
47
  return this.constructor.database;
@@ -72,7 +73,7 @@ class CollectionModel extends import_database.MagicAttributeModel {
72
73
  if (!this.db.inDialect("postgres") && collectionOptions.schema) {
73
74
  delete collectionOptions.schema;
74
75
  }
75
- if (this.db.inDialect("postgres") && !collectionOptions.schema && collectionOptions.from !== "db2cm") {
76
+ if (this.db.inDialect("postgres") && !collectionOptions.schema && !databaseSyncedCollectionSources.includes(collectionOptions.from)) {
76
77
  collectionOptions.schema = process.env.COLLECTION_MANAGER_SCHEMA || this.db.options.schema || "public";
77
78
  }
78
79
  if (this.db.hasCollection(name)) {
@@ -85,6 +85,9 @@ class CollectionRepository extends import_database.Repository {
85
85
  return true;
86
86
  }
87
87
  const fields = nameMap[instanceName].get("fields");
88
+ if (!Array.isArray(fields)) {
89
+ return [];
90
+ }
88
91
  return fields.filter((field) => field["type"] === "belongsTo" || field["type"] === "belongsToMany").map((field) => field.get("name"));
89
92
  })();
90
93
  if (import_lodash.default.isArray(skipField) && skipField.length) {
@@ -106,6 +109,9 @@ class CollectionRepository extends import_database.Repository {
106
109
  });
107
110
  const skipField = (() => {
108
111
  const fields = nameMap[viewCollectionName].get("fields");
112
+ if (!Array.isArray(fields)) {
113
+ return [];
114
+ }
109
115
  return fields.filter((field) => {
110
116
  var _a;
111
117
  if (((_a = field.options) == null ? void 0 : _a.source) && (field["type"] === "belongsTo" || field["type"] === "belongsToMany")) {
@@ -6,8 +6,13 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
+ export declare function syncCollectionFields(ctx: any, filterByTk: string, values: {
10
+ fields?: any[];
11
+ }): Promise<void>;
9
12
  declare const _default: {
10
13
  "collections:listMeta"(ctx: any, next: any): Promise<void>;
11
14
  "collections:setFields"(ctx: any, next: any): Promise<void>;
15
+ "collections:apply"(ctx: any, next: any): Promise<void>;
16
+ "fields:apply"(ctx: any, next: any): Promise<void>;
12
17
  };
13
18
  export default _default;
@@ -36,10 +36,79 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var collections_exports = {};
38
38
  __export(collections_exports, {
39
- default: () => collections_default
39
+ default: () => collections_default,
40
+ syncCollectionFields: () => syncCollectionFields
40
41
  });
41
42
  module.exports = __toCommonJS(collections_exports);
42
43
  var import_lodash = __toESM(require("lodash"));
44
+ var import_service = require("../modeling/service");
45
+ async function syncCollectionFields(ctx, filterByTk, values) {
46
+ var _a;
47
+ const transaction = await ctx.app.db.sequelize.transaction();
48
+ try {
49
+ const fields = (_a = values.fields) == null ? void 0 : _a.map((f) => {
50
+ delete f.key;
51
+ return f;
52
+ });
53
+ const db = ctx.app.db;
54
+ const collectionModel = await db.getRepository("collections").findOne({
55
+ filter: {
56
+ name: filterByTk
57
+ },
58
+ transaction
59
+ });
60
+ const existFields = await collectionModel.getFields({
61
+ transaction
62
+ });
63
+ const needUpdateFields = fields.filter((f) => {
64
+ return existFields.find((ef) => ef.name === f.name);
65
+ }).map((f) => {
66
+ return {
67
+ ...f,
68
+ key: existFields.find((ef) => ef.name === f.name).key
69
+ };
70
+ });
71
+ const needDestroyFields = existFields.filter((ef) => {
72
+ return !fields.find((f) => f.name === ef.name);
73
+ });
74
+ const needCreatedFields = fields.filter((f) => {
75
+ return !existFields.find((ef) => ef.name === f.name);
76
+ });
77
+ if (needDestroyFields.length) {
78
+ await db.getRepository("fields").destroy({
79
+ filterByTk: needDestroyFields.map((f) => f.key),
80
+ transaction
81
+ });
82
+ }
83
+ if (needUpdateFields.length) {
84
+ await db.getRepository("fields").updateMany({
85
+ records: needUpdateFields,
86
+ transaction
87
+ });
88
+ }
89
+ if (needCreatedFields.length) {
90
+ await db.getRepository("collections.fields", filterByTk).create({
91
+ values: needCreatedFields,
92
+ transaction
93
+ });
94
+ }
95
+ await collectionModel.loadFields({
96
+ transaction
97
+ });
98
+ const collection = db.getCollection(filterByTk);
99
+ await collection.sync({
100
+ force: false,
101
+ alter: {
102
+ drop: false
103
+ },
104
+ transaction
105
+ });
106
+ await transaction.commit();
107
+ } catch (e) {
108
+ await transaction.rollback();
109
+ throw e;
110
+ }
111
+ }
43
112
  var collections_default = {
44
113
  async ["collections:listMeta"](ctx, next) {
45
114
  const db = ctx.app.db;
@@ -69,72 +138,24 @@ var collections_default = {
69
138
  await next();
70
139
  },
71
140
  async ["collections:setFields"](ctx, next) {
72
- var _a;
73
141
  const { filterByTk, values } = ctx.action.params;
74
- const transaction = await ctx.app.db.sequelize.transaction();
75
- try {
76
- const fields = (_a = values.fields) == null ? void 0 : _a.map((f) => {
77
- delete f.key;
78
- return f;
79
- });
80
- const db = ctx.app.db;
81
- const collectionModel = await db.getRepository("collections").findOne({
82
- filter: {
83
- name: filterByTk
84
- },
85
- transaction
86
- });
87
- const existFields = await collectionModel.getFields({
88
- transaction
89
- });
90
- const needUpdateFields = fields.filter((f) => {
91
- return existFields.find((ef) => ef.name === f.name);
92
- }).map((f) => {
93
- return {
94
- ...f,
95
- key: existFields.find((ef) => ef.name === f.name).key
96
- };
97
- });
98
- const needDestroyFields = existFields.filter((ef) => {
99
- return !fields.find((f) => f.name === ef.name);
100
- });
101
- const needCreatedFields = fields.filter((f) => {
102
- return !existFields.find((ef) => ef.name === f.name);
103
- });
104
- if (needDestroyFields.length) {
105
- await db.getRepository("fields").destroy({
106
- filterByTk: needDestroyFields.map((f) => f.key),
107
- transaction
108
- });
109
- }
110
- if (needUpdateFields.length) {
111
- await db.getRepository("fields").updateMany({
112
- records: needUpdateFields,
113
- transaction
114
- });
115
- }
116
- if (needCreatedFields.length) {
117
- await db.getRepository("collections.fields", filterByTk).create({
118
- values: needCreatedFields,
119
- transaction
120
- });
121
- }
122
- await collectionModel.loadFields({
123
- transaction
124
- });
125
- const collection = db.getCollection(filterByTk);
126
- await collection.sync({
127
- force: false,
128
- alter: {
129
- drop: false
130
- },
131
- transaction
132
- });
133
- await transaction.commit();
134
- } catch (e) {
135
- await transaction.rollback();
136
- throw e;
137
- }
142
+ await syncCollectionFields(ctx, filterByTk, values);
143
+ await next();
144
+ },
145
+ async ["collections:apply"](ctx, next) {
146
+ const rawValues = ctx.action.params.values || ctx.request.body || {};
147
+ ctx.body = await (0, import_service.applyCollectionDefinition)(ctx, rawValues, syncCollectionFields);
148
+ await next();
149
+ },
150
+ async ["fields:apply"](ctx, next) {
151
+ const rawValues = ctx.action.params.values || ctx.request.body || {};
152
+ ctx.body = {
153
+ data: await (0, import_service.applyFieldDefinition)(ctx, rawValues)
154
+ };
138
155
  await next();
139
156
  }
140
157
  };
158
+ // Annotate the CommonJS export names for ESM import in node:
159
+ 0 && (module.exports = {
160
+ syncCollectionFields
161
+ });
@@ -56,6 +56,7 @@ var import_models = require("./models");
56
56
  var import_collections = __toESM(require("./resourcers/collections"));
57
57
  var import_views = __toESM(require("./resourcers/views"));
58
58
  var import_main_data_source = __toESM(require("./resourcers/main-data-source"));
59
+ var import_mcp_post_processors = require("./mcp-post-processors");
59
60
  var import_constants = require("./constants");
60
61
  var import_json_schema = require("@formily/json-schema");
61
62
  var import_lodash2 = __toESM(require("lodash"));
@@ -109,7 +110,7 @@ class PluginDataSourceMainServer extends import_server.Plugin {
109
110
  });
110
111
  this.app.db.on("collections.beforeCreate", (0, import_beforeCreateForViewCollection.beforeCreateForViewCollection)(this.db));
111
112
  this.app.db.on("collections.beforeCreate", async (model, options) => {
112
- if (this.app.db.getCollection(model.get("name")) && model.get("from") !== "db2cm" && !model.get("isThrough")) {
113
+ if (this.app.db.getCollection(model.get("name")) && !["db2cm", "dbsync"].includes(model.get("from")) && !model.get("isThrough")) {
113
114
  throw new Error(`Collection named ${model.get("name")} already exists`);
114
115
  }
115
116
  });
@@ -378,7 +379,7 @@ class PluginDataSourceMainServer extends import_server.Plugin {
378
379
  this.app.acl.allow("collectionCategories", "list", "loggedIn");
379
380
  this.app.acl.registerSnippet({
380
381
  name: `pm.data-source-manager.data-source-main`,
381
- actions: ["collections:*", "collections.fields:*", "collectionCategories:*", "mainDataSource:*"]
382
+ actions: ["collections:*", "collections.fields:*", "fields:*", "collectionCategories:*", "mainDataSource:*"]
382
383
  });
383
384
  this.app.acl.registerSnippet({
384
385
  name: `pm.data-source-manager.collection-view `,
@@ -404,6 +405,8 @@ class PluginDataSourceMainServer extends import_server.Plugin {
404
405
  async load() {
405
406
  this.db.getRepository("collections").setApp(this.app);
406
407
  this.registerErrorHandler();
408
+ (0, import_mcp_post_processors.registerDataSourceMainMcpPostProcessors)(this.ai.mcpToolsManager);
409
+ this.app.auditManager.registerActions(["collections:apply", "fields:apply"]);
407
410
  this.app.resourceManager.use(async function mergeReverseFieldWhenSaveCollectionField(ctx, next) {
408
411
  if (ctx.action.resourceName === "collections.fields" && ["create", "update"].includes(ctx.action.actionName)) {
409
412
  ctx.action.mergeParams({