diff --git a/CHANGELOG.md b/CHANGELOG.md index 54a464dc2..8b1b464a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v1.0.0-103 + + +### 🚀 Enhancements + +- Dashboard on field deleted ([691cff2](https://github.com/undb-io/undb/commit/691cff2)) + +### ❤️ Contributors + +- Nichenqin ([@nichenqin](http://github.com/nichenqin)) + ## v1.0.0-102 diff --git a/apps/backend/drizzle/0010_nostalgic_nehzno.sql b/apps/backend/drizzle/0010_nostalgic_nehzno.sql new file mode 100644 index 000000000..446aff96a --- /dev/null +++ b/apps/backend/drizzle/0010_nostalgic_nehzno.sql @@ -0,0 +1,7 @@ +CREATE TABLE `undb_dashboard_table_id_mapping` ( + `dashboard_id` text NOT NULL, + `table_id` text NOT NULL, + PRIMARY KEY(`dashboard_id`, `table_id`), + FOREIGN KEY (`dashboard_id`) REFERENCES `undb_dashboard`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`table_id`) REFERENCES `undb_table`(`id`) ON UPDATE no action ON DELETE no action +); diff --git a/apps/backend/drizzle/meta/0010_snapshot.json b/apps/backend/drizzle/meta/0010_snapshot.json new file mode 100644 index 000000000..f31e77d89 --- /dev/null +++ b/apps/backend/drizzle/meta/0010_snapshot.json @@ -0,0 +1,2033 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7ec06d2c-aecd-49d1-9319-a74dab1de357", + "prevId": "371c72b4-7281-4229-a089-c91d645a0fde", + "tables": { + "undb_api_token": { + "name": "undb_api_token", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_api_token_token_unique": { + "name": "undb_api_token_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "api_token_space_id_idx": { + "name": "api_token_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "api_token_user_id_idx": { + "name": "api_token_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_api_token_user_id_undb_user_id_fk": { + "name": "undb_api_token_user_id_undb_user_id_fk", + "tableFrom": "undb_api_token", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_api_token_space_id_undb_space_id_fk": { + "name": "undb_api_token_space_id_undb_space_id_fk", + "tableFrom": "undb_api_token", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_attachment_mapping": { + "name": "undb_attachment_mapping", + "columns": { + "attachment_id": { + "name": "attachment_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_id": { + "name": "record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_attachment_mapping_attachment_id_undb_attachment_id_fk": { + "name": "undb_attachment_mapping_attachment_id_undb_attachment_id_fk", + "tableFrom": "undb_attachment_mapping", + "tableTo": "undb_attachment", + "columnsFrom": [ + "attachment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_attachment_mapping_table_id_undb_table_id_fk": { + "name": "undb_attachment_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_attachment_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_attachment_mapping_attachment_id_table_id_record_id_field_id_pk": { + "columns": [ + "attachment_id", + "table_id", + "record_id", + "field_id" + ], + "name": "undb_attachment_mapping_attachment_id_table_id_record_id_field_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_attachment": { + "name": "undb_attachment", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "attachment_size_idx": { + "name": "attachment_size_idx", + "columns": [ + "size" + ], + "isUnique": false + }, + "attachment_space_id_idx": { + "name": "attachment_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_attachment_created_by_undb_user_id_fk": { + "name": "undb_attachment_created_by_undb_user_id_fk", + "tableFrom": "undb_attachment", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_attachment_space_id_undb_space_id_fk": { + "name": "undb_attachment_space_id_undb_space_id_fk", + "tableFrom": "undb_attachment", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_audit": { + "name": "undb_audit", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "op": { + "name": "op", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_id": { + "name": "record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "audit_table_id_idx": { + "name": "audit_table_id_idx", + "columns": [ + "table_id" + ], + "isUnique": false + }, + "audit_space_id_idx": { + "name": "audit_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "audit_record_id_idx": { + "name": "audit_record_id_idx", + "columns": [ + "record_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_audit_space_id_undb_space_id_fk": { + "name": "undb_audit_space_id_undb_space_id_fk", + "tableFrom": "undb_audit", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_base": { + "name": "undb_base", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "base_space_id_idx": { + "name": "base_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "base_name_unique_idx": { + "name": "base_name_unique_idx", + "columns": [ + "name", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_base_space_id_undb_space_id_fk": { + "name": "undb_base_space_id_undb_space_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_base_created_by_undb_user_id_fk": { + "name": "undb_base_created_by_undb_user_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_base_updated_by_undb_user_id_fk": { + "name": "undb_base_updated_by_undb_user_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_dashboard_table_id_mapping": { + "name": "undb_dashboard_table_id_mapping", + "columns": { + "dashboard_id": { + "name": "dashboard_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_dashboard_table_id_mapping_dashboard_id_undb_dashboard_id_fk": { + "name": "undb_dashboard_table_id_mapping_dashboard_id_undb_dashboard_id_fk", + "tableFrom": "undb_dashboard_table_id_mapping", + "tableTo": "undb_dashboard", + "columnsFrom": [ + "dashboard_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_dashboard_table_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_dashboard_table_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_dashboard_table_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_dashboard_table_id_mapping_dashboard_id_table_id_pk": { + "columns": [ + "dashboard_id", + "table_id" + ], + "name": "undb_dashboard_table_id_mapping_dashboard_id_table_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_dashboard": { + "name": "undb_dashboard", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_id": { + "name": "base_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "widgets": { + "name": "widgets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "layout": { + "name": "layout", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "dashboard_base_id_idx": { + "name": "dashboard_base_id_idx", + "columns": [ + "base_id" + ], + "isUnique": false + }, + "dashboard_space_id_idx": { + "name": "dashboard_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "dashboard_name_unique_idx": { + "name": "dashboard_name_unique_idx", + "columns": [ + "name", + "base_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_dashboard_base_id_undb_base_id_fk": { + "name": "undb_dashboard_base_id_undb_base_id_fk", + "tableFrom": "undb_dashboard", + "tableTo": "undb_base", + "columnsFrom": [ + "base_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_dashboard_space_id_undb_space_id_fk": { + "name": "undb_dashboard_space_id_undb_space_id_fk", + "tableFrom": "undb_dashboard", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_dashboard_created_by_undb_user_id_fk": { + "name": "undb_dashboard_created_by_undb_user_id_fk", + "tableFrom": "undb_dashboard", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_dashboard_updated_by_undb_user_id_fk": { + "name": "undb_dashboard_updated_by_undb_user_id_fk", + "tableFrom": "undb_dashboard", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_email_verification_code": { + "name": "undb_email_verification_code", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_email_verification_code_user_id_unique": { + "name": "undb_email_verification_code_user_id_unique", + "columns": [ + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_email_verification_code_user_id_undb_user_id_fk": { + "name": "undb_email_verification_code_user_id_undb_user_id_fk", + "tableFrom": "undb_email_verification_code", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_invitation": { + "name": "undb_invitation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invited_at": { + "name": "invited_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invitation_space_id_idx": { + "name": "invitation_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "invitation_unique_idx": { + "name": "invitation_unique_idx", + "columns": [ + "email", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_invitation_space_id_undb_space_id_fk": { + "name": "undb_invitation_space_id_undb_space_id_fk", + "tableFrom": "undb_invitation", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_invitation_inviter_id_undb_user_id_fk": { + "name": "undb_invitation_inviter_id_undb_user_id_fk", + "tableFrom": "undb_invitation", + "tableTo": "undb_user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_oauth_account": { + "name": "undb_oauth_account", + "columns": { + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_oauth_account_user_id_undb_user_id_fk": { + "name": "undb_oauth_account_user_id_undb_user_id_fk", + "tableFrom": "undb_oauth_account", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_oauth_account_provider_id_provider_user_id_pk": { + "columns": [ + "provider_id", + "provider_user_id" + ], + "name": "undb_oauth_account_provider_id_provider_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_outbox": { + "name": "undb_outbox", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "outbox_space_id_idx": { + "name": "outbox_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_outbox_space_id_undb_space_id_fk": { + "name": "undb_outbox_space_id_undb_space_id_fk", + "tableFrom": "undb_outbox", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_password_reset_token": { + "name": "undb_password_reset_token", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_password_reset_token_token_unique": { + "name": "undb_password_reset_token_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "password_reset_token_user_id_idx": { + "name": "password_reset_token_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_password_reset_token_user_id_undb_user_id_fk": { + "name": "undb_password_reset_token_user_id_undb_user_id_fk", + "tableFrom": "undb_password_reset_token", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_reference_id_mapping": { + "name": "undb_reference_id_mapping", + "columns": { + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "symmetric_field_id": { + "name": "symmetric_field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "foreign_table_id": { + "name": "foreign_table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "reference_id_mapping_unique_idx": { + "name": "reference_id_mapping_unique_idx", + "columns": [ + "field_id", + "table_id", + "symmetric_field_id", + "foreign_table_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_reference_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_reference_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_reference_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_reference_id_mapping_foreign_table_id_undb_table_id_fk": { + "name": "undb_reference_id_mapping_foreign_table_id_undb_table_id_fk", + "tableFrom": "undb_reference_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "foreign_table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_rollup_id_mapping": { + "name": "undb_rollup_id_mapping", + "columns": { + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rollup_id": { + "name": "rollup_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rollup_table_id": { + "name": "rollup_table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_rollup_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_rollup_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_rollup_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_rollup_id_mapping_rollup_table_id_undb_table_id_fk": { + "name": "undb_rollup_id_mapping_rollup_table_id_undb_table_id_fk", + "tableFrom": "undb_rollup_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "rollup_table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_rollup_id_mapping_field_id_rollup_id_pk": { + "columns": [ + "field_id", + "rollup_id" + ], + "name": "undb_rollup_id_mapping_field_id_rollup_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_session": { + "name": "undb_session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_session_user_id_undb_user_id_fk": { + "name": "undb_session_user_id_undb_user_id_fk", + "tableFrom": "undb_session", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_session_space_id_undb_space_id_fk": { + "name": "undb_session_space_id_undb_space_id_fk", + "tableFrom": "undb_session", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_share": { + "name": "undb_share", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "share_space_id_idx": { + "name": "share_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "share_unique_idx": { + "name": "share_unique_idx", + "columns": [ + "target_type", + "target_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_share_space_id_undb_space_id_fk": { + "name": "undb_share_space_id_undb_space_id_fk", + "tableFrom": "undb_share", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_space": { + "name": "undb_space", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_personal": { + "name": "is_personal", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "space_name_idx": { + "name": "space_name_idx", + "columns": [ + "name" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_space_created_by_undb_user_id_fk": { + "name": "undb_space_created_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_updated_by_undb_user_id_fk": { + "name": "undb_space_updated_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_deleted_by_undb_user_id_fk": { + "name": "undb_space_deleted_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_space_member": { + "name": "undb_space_member", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "space_member_unique_idx": { + "name": "space_member_unique_idx", + "columns": [ + "user_id", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_space_member_user_id_undb_user_id_fk": { + "name": "undb_space_member_user_id_undb_user_id_fk", + "tableFrom": "undb_space_member", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_member_space_id_undb_space_id_fk": { + "name": "undb_space_member_space_id_undb_space_id_fk", + "tableFrom": "undb_space_member", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_table_id_mapping": { + "name": "undb_table_id_mapping", + "columns": { + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_table_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_table_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_table_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_table_id_mapping_table_id_subject_id_pk": { + "columns": [ + "table_id", + "subject_id" + ], + "name": "undb_table_id_mapping_table_id_subject_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_table": { + "name": "undb_table", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_id": { + "name": "base_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "views": { + "name": "views", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forms": { + "name": "forms", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rls": { + "name": "rls", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "widgets": { + "name": "widgets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "table_base_id_idx": { + "name": "table_base_id_idx", + "columns": [ + "base_id" + ], + "isUnique": false + }, + "table_space_id_idx": { + "name": "table_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "table_name_unique_idx": { + "name": "table_name_unique_idx", + "columns": [ + "name", + "base_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_table_base_id_undb_base_id_fk": { + "name": "undb_table_base_id_undb_base_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_base", + "columnsFrom": [ + "base_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_space_id_undb_space_id_fk": { + "name": "undb_table_space_id_undb_space_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_created_by_undb_user_id_fk": { + "name": "undb_table_created_by_undb_user_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_updated_by_undb_user_id_fk": { + "name": "undb_table_updated_by_undb_user_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_user": { + "name": "undb_user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "undb_user_email_unique": { + "name": "undb_user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "user_username_idx": { + "name": "user_username_idx", + "columns": [ + "username" + ], + "isUnique": false + }, + "user_email_idx": { + "name": "user_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "undb_webhook": { + "name": "undb_webhook", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "headers": { + "name": "headers", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "condition": { + "name": "condition", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "webhook_table_id_idx": { + "name": "webhook_table_id_idx", + "columns": [ + "table_id" + ], + "isUnique": false + }, + "webhook_space_id_idx": { + "name": "webhook_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "webhook_url_idx": { + "name": "webhook_url_idx", + "columns": [ + "url" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_webhook_table_id_undb_table_id_fk": { + "name": "undb_webhook_table_id_undb_table_id_fk", + "tableFrom": "undb_webhook", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_webhook_space_id_undb_space_id_fk": { + "name": "undb_webhook_space_id_undb_space_id_fk", + "tableFrom": "undb_webhook", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/apps/backend/drizzle/meta/_journal.json b/apps/backend/drizzle/meta/_journal.json index 1a97e3e97..dd4218335 100644 --- a/apps/backend/drizzle/meta/_journal.json +++ b/apps/backend/drizzle/meta/_journal.json @@ -71,6 +71,13 @@ "when": 1728874533782, "tag": "0009_workable_scorpion", "breakpoints": true + }, + { + "idx": 10, + "version": "6", + "when": 1729306799169, + "tag": "0010_nostalgic_nehzno", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/backend/package.json b/apps/backend/package.json index 2081d5cff..2317232e4 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -8,8 +8,8 @@ "build:docker": "bun build --compile src/index.ts --target=bun --packages=external --sourcemap --outfile undb" }, "dependencies": { - "@aws-sdk/client-s3": "^3.673.0", - "@aws-sdk/s3-request-presigner": "^3.673.0", + "@aws-sdk/client-s3": "^3.674.0", + "@aws-sdk/s3-request-presigner": "^3.674.0", "@elysiajs/cors": "1.1.0", "@elysiajs/cron": "1.1.0", "@elysiajs/html": "1.1.0", @@ -25,6 +25,7 @@ "@undb/authz": "workspace:*", "@undb/base": "workspace:*", "@undb/command-handlers": "workspace:*", + "@undb/event-handlers": "workspace:*", "@undb/context": "workspace:*", "@undb/cqrs": "workspace:*", "@undb/dashboard": "workspace:*", @@ -41,7 +42,7 @@ "@undb/trpc": "workspace:*", "@undb/webhook": "workspace:*", "arctic": "^1.9.2", - "bun": "^1.1.30", + "bun": "^1.1.31", "core-js": "^3.38.1", "elysia": "1.1.7", "got": "^14.4.3", diff --git a/apps/backend/src/registry/index.ts b/apps/backend/src/registry/index.ts index 3da334fcd..45b06a497 100644 --- a/apps/backend/src/registry/index.ts +++ b/apps/backend/src/registry/index.ts @@ -1,6 +1,7 @@ import { registerCommands } from "@undb/command-handlers" import { registerQueries } from "@undb/query-handlers" +import { registerEvents } from "@undb/event-handlers" import { registerWebhook } from "../modules" import { registerStorage } from "../modules/file/storage" import { registerMail } from "../modules/mail/mail.register" @@ -13,6 +14,7 @@ export const register = () => { registerContext() registerDb() registerCommands() + registerEvents() registerQueries() registerWebhook() } diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 854ec4a19..c560e59e0 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -27,8 +27,8 @@ "@types/lodash.unzip": "^3.4.9", "@types/papaparse": "^5.3.14", "@types/sortablejs": "latest", - "@typescript-eslint/eslint-plugin": "^8.9.0", - "@typescript-eslint/parser": "^8.9.0", + "@typescript-eslint/eslint-plugin": "^8.10.0", + "@typescript-eslint/parser": "^8.10.0", "@undb/commands": "workspace:*", "@undb/domain": "workspace:*", "@undb/i18n": "workspace:*", diff --git a/bun.lockb b/bun.lockb index b2f4e6e80..e0c26bf31 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 000000000..0041c35c5 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,3 @@ +[install] +# set default registry as a string +registry = "https://registry.npmmirror.com" \ No newline at end of file diff --git a/package.json b/package.json index 41a24da11..6f0c0f013 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "undb", - "version": "1.0.0-102", + "version": "1.0.0-103", "private": true, "scripts": { "build": "NODE_ENV=production bun --bun turbo build", @@ -23,7 +23,7 @@ "drizzle-kit": "^0.26.2", "husky": "^9.1.6", "lint-staged": "^15.2.10", - "npm-run-all2": "^6.2.3", + "npm-run-all2": "^6.2.4", "prettier": "^3.3.3", "turbo": "^2.1.3" }, @@ -33,7 +33,7 @@ "engines": { "node": ">=18" }, - "packageManager": "bun@1.1.30", + "packageManager": "bun@1.1.31", "workspaces": [ "apps/*", "packages/*" diff --git a/packages/base/src/events/base-created.event.ts b/packages/base/src/events/base-created.event.ts index 763240999..ff3190fed 100644 --- a/packages/base/src/events/base-created.event.ts +++ b/packages/base/src/events/base-created.event.ts @@ -13,7 +13,10 @@ export type IBaseCreatedEventPayload = z.infer export class BaseCreatedEvent extends BaseEvent { name = EVT_BASE_CREATED - constructor(public readonly payload: IBaseCreatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IBaseCreatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/base/src/events/base-updated.event.ts b/packages/base/src/events/base-updated.event.ts index b67283c0a..2e5d11462 100644 --- a/packages/base/src/events/base-updated.event.ts +++ b/packages/base/src/events/base-updated.event.ts @@ -14,7 +14,7 @@ export type IBaseUpdatedEventPayload = z.infer export class BaseUpdatedEvent extends BaseEvent { name = EVT_BASE_UPDATED - constructor(public readonly payload: IBaseUpdatedEventPayload) { - super(payload, undefined) + constructor(public readonly payload: IBaseUpdatedEventPayload, spaceId: string ) { + super(payload, undefined, spaceId) } } diff --git a/packages/cqrs/src/decorators/constants.ts b/packages/cqrs/src/decorators/constants.ts index f8a8d61b5..50b82221e 100644 --- a/packages/cqrs/src/decorators/constants.ts +++ b/packages/cqrs/src/decorators/constants.ts @@ -3,3 +3,6 @@ export const COMMAND_HANDLER_METADATA = Symbol.for("COMMAND_HANDLER") export const QUERY_METADATA = Symbol.for("QUERY") export const QUERY_HANDLER_METADATA = Symbol.for("QUERY_HANDLER") + +export const EVENT_METADATA = Symbol.for("EVENT") +export const EVENT_HANDLER_METADATA = Symbol.for("EVENT_HANDLER") diff --git a/packages/cqrs/src/decorators/event-handler.decorator.ts b/packages/cqrs/src/decorators/event-handler.decorator.ts new file mode 100644 index 000000000..3f13bcb21 --- /dev/null +++ b/packages/cqrs/src/decorators/event-handler.decorator.ts @@ -0,0 +1,15 @@ +import "reflect-metadata" + +import type { BaseEvent } from "@undb/domain" +import type { Class } from "type-fest" +import { v4 } from "uuid" +import { EVENT_HANDLER_METADATA, EVENT_METADATA } from "./constants" + +export const eventHandler = (event: Class): ClassDecorator => { + return (target: object) => { + if (!Reflect.hasOwnMetadata(EVENT_METADATA, event)) { + Reflect.defineMetadata(EVENT_METADATA, { id: v4() }, event) + } + Reflect.defineMetadata(EVENT_HANDLER_METADATA, event, target) + } +} diff --git a/packages/cqrs/src/decorators/index.ts b/packages/cqrs/src/decorators/index.ts index 10a2c4b20..7c2343730 100644 --- a/packages/cqrs/src/decorators/index.ts +++ b/packages/cqrs/src/decorators/index.ts @@ -1,2 +1,3 @@ export * from "./command-handler.decorator" +export * from "./event-handler.decorator" export * from "./query-handler.decorator" diff --git a/packages/cqrs/src/default-event-publisher.ts b/packages/cqrs/src/default-event-publisher.ts new file mode 100644 index 000000000..b147bbd8b --- /dev/null +++ b/packages/cqrs/src/default-event-publisher.ts @@ -0,0 +1,14 @@ +import type { BaseEvent, IEventPublisher } from "@undb/domain" +import { Subject } from "rxjs" + +export class DefaultEventPubSub implements IEventPublisher { + constructor(public subject$: Subject) {} + + async publish(event: E): Promise { + this.subject$.next(event) + } + + async publishMany(events: E[]): Promise { + events.forEach((event) => this.subject$.next(event)) + } +} diff --git a/packages/cqrs/src/event-bus.ts b/packages/cqrs/src/event-bus.ts new file mode 100644 index 000000000..8f9dccf13 --- /dev/null +++ b/packages/cqrs/src/event-bus.ts @@ -0,0 +1,75 @@ +import { container, singleton } from "@undb/di" +import type { BaseEvent, EventMetadata, IEventBus, IEventHandler, IEventPublisher } from "@undb/domain" +import { Subject } from "rxjs" +import type { Class } from "type-fest" +import { EVENT_HANDLER_METADATA, EVENT_METADATA } from "./decorators/constants" +import { DefaultEventPubSub } from "./default-event-publisher" +import { InvalidEventHandlerException } from "./exceptions/invalid-event-handler.exception" + +export type EventHandlerType = Class> + +@singleton() +export class EventBus implements IEventBus { + private subject = new Subject() + private readonly publisher: IEventPublisher = new DefaultEventPubSub(this.subject) + + #handlers = new Map>() + + async publish(event: TEvent): Promise { + const eventId = this.getEventId(event) + if (!eventId) { + return + } + + const handler = this.#handlers.get(eventId) + if (!handler) { + return + } + this.publisher.publish(event) + return handler.handle(event) + } + + async publishMany(events: TEvent[]): Promise { + await Promise.all(events.map((event) => this.publish(event))) + } + + register(handlers: EventHandlerType[]) { + handlers.forEach((handler) => this.registerHandler(handler)) + } + + private bind(handler: IEventHandler, id: string) { + this.#handlers.set(id, handler) + } + + protected registerHandler(handler: EventHandlerType) { + const instance = container.resolve(handler) + if (!instance) { + return + } + const target = this.reflectEventId(handler) + if (!target) { + throw new InvalidEventHandlerException() + } + this.bind(instance as IEventHandler, target) + } + + private reflectEventId(handler: EventHandlerType): string | undefined { + const event: BaseEvent = Reflect.getMetadata(EVENT_HANDLER_METADATA, handler) + const eventMetadata: EventMetadata = Reflect.getMetadata(EVENT_METADATA, event) + return eventMetadata.id + } + + private getEventId(event: TEvent): string | undefined { + const { constructor: eventType } = Object.getPrototypeOf(event) + const eventMetadata: EventMetadata = Reflect.getMetadata(EVENT_METADATA, eventType) + if (!eventMetadata) { + return + } + return eventMetadata.id + } + + private getEventName(event: TEvent): string { + const { constructor } = Object.getPrototypeOf(event) + return constructor.name as string + } +} diff --git a/packages/cqrs/src/exceptions/event-handler-not-found.exception.ts b/packages/cqrs/src/exceptions/event-handler-not-found.exception.ts new file mode 100644 index 000000000..395aef23f --- /dev/null +++ b/packages/cqrs/src/exceptions/event-handler-not-found.exception.ts @@ -0,0 +1,5 @@ +export class EventHandlerNotFoundException extends Error { + constructor(eventId: string) { + super(`The event handler for the "${eventId}" event was not found!`) + } +} diff --git a/packages/cqrs/src/exceptions/invalid-event-handler.exception.ts b/packages/cqrs/src/exceptions/invalid-event-handler.exception.ts new file mode 100644 index 000000000..4c0ef5527 --- /dev/null +++ b/packages/cqrs/src/exceptions/invalid-event-handler.exception.ts @@ -0,0 +1,5 @@ +export class InvalidEventHandlerException extends Error { + constructor() { + super(`Invalid event handler exception (missing @eventHandler() decorator?)`) + } +} diff --git a/packages/cqrs/src/index.ts b/packages/cqrs/src/index.ts index 70cafddb4..ef2155361 100644 --- a/packages/cqrs/src/index.ts +++ b/packages/cqrs/src/index.ts @@ -1,3 +1,4 @@ export * from "./command-bus" export * from "./decorators" +export * from "./event-bus" export * from "./query-bus" diff --git a/packages/dashboard/src/dashboard.do.ts b/packages/dashboard/src/dashboard.do.ts index 530e29d00..3c7814247 100644 --- a/packages/dashboard/src/dashboard.do.ts +++ b/packages/dashboard/src/dashboard.do.ts @@ -68,6 +68,14 @@ export class Dashboard extends AggregateRoot { return Some(spec) } + get tableIds(): string[] { + return this.widgets.tableIds + } + + $onFieldDeleted(tableId: string, fieldId: string): Option { + return this.widgets.$onFieldDeleted(tableId, fieldId) + } + public toJSON(): IDashboardDTO { return { id: this.id.value, diff --git a/packages/dashboard/src/interface.ts b/packages/dashboard/src/interface.ts index 569566fc7..b5c7910f7 100644 --- a/packages/dashboard/src/interface.ts +++ b/packages/dashboard/src/interface.ts @@ -6,6 +6,7 @@ import type { WithDashboardLayout } from "./specifications/dashboard-layout.spec import type { WithDashboardName } from "./specifications/dashboard-name.specification.js" import type { WithDashboardQ } from "./specifications/dashboard-q.specification.js" import type { WithDashboardSpaceId } from "./specifications/dashboard-space-id.specification.js" +import type { DashboardTableIdSpecification } from "./specifications/dashboard-table-id.specification.js" import type { WithDashboardWidgets } from "./specifications/dashboard-widget.specification.js" import type { DuplicatedDashboardSpecification } from "./specifications/dashboard.specification.js" @@ -13,6 +14,7 @@ export interface IDashboardSpecVisitor extends ISpecVisitor { withId(v: WithDashboardId): void withDashboardSpaceId(v: WithDashboardSpaceId): void withDashboardBaseId(v: DashboardBaseIdSpecification): void + withDashboardTableId(v: DashboardTableIdSpecification): void duplicatedDashboard(v: DuplicatedDashboardSpecification): void withDashboardWidgets(v: WithDashboardWidgets): void withDashboardLayout(v: WithDashboardLayout): void diff --git a/packages/dashboard/src/services/dashboard.event-service.ts b/packages/dashboard/src/services/dashboard.event-service.ts new file mode 100644 index 000000000..8ff77c5d9 --- /dev/null +++ b/packages/dashboard/src/services/dashboard.event-service.ts @@ -0,0 +1,21 @@ +import { createLogger } from "@undb/logger" +import type { FieldDeletedEvent } from "@undb/table" +import type { Dashboard } from "../dashboard.do" +import { injectDashboardRepository, type IDashboardRepository } from "../dashboard.repository" + +export interface IDashboardEventService { + onFieldDeleted(dashboard: Dashboard, event: FieldDeletedEvent): Promise +} + +export class DashboardEventService implements IDashboardEventService { + private readonly logger = createLogger(DashboardEventService.name) + + constructor( + @injectDashboardRepository() + private readonly dashboardRepository: IDashboardRepository, + ) {} + + async onFieldDeleted(dashboard: Dashboard, event: FieldDeletedEvent): Promise { + this.logger.debug(event) + } +} diff --git a/packages/dashboard/src/specifications/dashboard-table-id.specification.ts b/packages/dashboard/src/specifications/dashboard-table-id.specification.ts new file mode 100644 index 000000000..d836b89ba --- /dev/null +++ b/packages/dashboard/src/specifications/dashboard-table-id.specification.ts @@ -0,0 +1,19 @@ +import { CompositeSpecification, Ok, type Result } from "@undb/domain" +import type { Dashboard } from "../dashboard.do" +import type { IDashboardSpecVisitor } from "../interface" + +export class DashboardTableIdSpecification extends CompositeSpecification { + constructor(public readonly tableId: string) { + super() + } + isSatisfiedBy(t: Dashboard): boolean { + throw new Error("Method not implemented.") + } + mutate(t: Dashboard): Result { + throw new Error("Method not implemented.") + } + accept(v: IDashboardSpecVisitor): Result { + v.withDashboardTableId(this) + return Ok(undefined) + } +} diff --git a/packages/dashboard/src/specifications/index.ts b/packages/dashboard/src/specifications/index.ts index ee19c74f1..fc4dcbc98 100644 --- a/packages/dashboard/src/specifications/index.ts +++ b/packages/dashboard/src/specifications/index.ts @@ -4,10 +4,11 @@ export * from "./dashboard-layout.specification.js" export * from "./dashboard-name.specification.js" export * from "./dashboard-q.specification.js" export * from "./dashboard-space-id.specification.js" +export * from './dashboard-table-id.specification.js' export * from "./dashboard-widget.specification.js" export * from "./dashboard.specification.js" -import { CompositeSpecification, Err, Ok, Result } from "@undb/domain" +import { CompositeSpecification,Err,Ok,Result } from "@undb/domain" import type { Dashboard } from "../dashboard.do.js" import type { IUniqueDashboardDTO } from "../dto/unique-dashboard.dto.js" import type { IDashboardSpecVisitor } from "../interface.js" diff --git a/packages/dashboard/src/value-objects/dashboard-widgets.vo.ts b/packages/dashboard/src/value-objects/dashboard-widgets.vo.ts index a02b64a9d..47ebdd31c 100644 --- a/packages/dashboard/src/value-objects/dashboard-widgets.vo.ts +++ b/packages/dashboard/src/value-objects/dashboard-widgets.vo.ts @@ -1,4 +1,4 @@ -import { Option, Some, ValueObject } from "@undb/domain" +import { None, Option, Some, ValueObject } from "@undb/domain" import { TableDo, tableId, widgetDTO, WidgetVO, type IWidgetDTO } from "@undb/table" import * as z from "@undb/zod" import type { IUpdateDashboardWidgetDTO } from "../dto/update-dashboard-widget.dto" @@ -23,6 +23,31 @@ export class DashboardWidget extends ValueObject { }) } + get tableId() { + return this.props.table.id + } + + deleteField(tableId: string, fieldId: string): Option { + if (this.tableId !== tableId) { + return None + } + + const widget = WidgetVO.fromJSON(this.props.widget) + const updated = widget.deleteField(fieldId) + if (updated.isNone()) { + return None + } + + return Some( + new DashboardWidget({ + table: { + id: this.props.table.id, + }, + widget: updated.unwrap().toJSON(), + }), + ) + } + /** * Creates a DashboardWidget instance * @param table Table object @@ -102,6 +127,20 @@ export class DashboardWidgets extends ValueObject { return new DashboardWidgets(widgets.map((w) => w.toJSON())) } + $onFieldDeleted(tableId: string, fieldId: string): Option { + const widgets = this.value.map((w) => { + const widget = new DashboardWidget(w) + const updated = widget.deleteField(tableId, fieldId) + return updated.isSome() ? updated.unwrap().toJSON() : w + }) + const dashbaordWidgets = new DashboardWidgets(widgets) + return Some(new WithDashboardWidgets(dashbaordWidgets)) + } + + get tableIds(): string[] { + return this.value.map((w) => w.table.id).filter((tableId) => !!tableId) as string[] + } + addWidget(table: TableDo, dto: IDashboardWidget): DashboardWidgets { const widget = DashboardWidget.from(table, dto.widget) return new DashboardWidgets([...this.value, widget.toJSON()]) diff --git a/packages/domain/src/event-bus.ts b/packages/domain/src/event-bus.ts new file mode 100644 index 000000000..66f568720 --- /dev/null +++ b/packages/domain/src/event-bus.ts @@ -0,0 +1,6 @@ +import type { BaseEvent } from "./event.js" + +export interface IEventBus { + publish(event: TEvent): Promise + publishMany(events: TEvent[]): Promise +} diff --git a/packages/domain/src/event-metadata.ts b/packages/domain/src/event-metadata.ts new file mode 100644 index 000000000..d481a9574 --- /dev/null +++ b/packages/domain/src/event-metadata.ts @@ -0,0 +1,3 @@ +export interface EventMetadata { + id: string +} diff --git a/packages/domain/src/event-publisher.ts b/packages/domain/src/event-publisher.ts new file mode 100644 index 000000000..b56205080 --- /dev/null +++ b/packages/domain/src/event-publisher.ts @@ -0,0 +1,6 @@ +import type { BaseEvent } from "./event" + +export interface IEventPublisher { + publish(event: TEvent): Promise + publishMany(events: TEvent[]): Promise +} diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts index d78a9a5c5..46e99f6a7 100644 --- a/packages/domain/src/index.ts +++ b/packages/domain/src/index.ts @@ -8,7 +8,10 @@ export * from "./command.js" export * from "./date.vo.js" export * from "./email.vo.js" export * from "./entity.base.js" +export * from "./event-bus.js" export * from "./event-handler.js" +export * from "./event-metadata.js" +export * from "./event-publisher.js" export * from "./event.js" export * from "./exception.base.js" export * from "./exceptions" diff --git a/packages/event-handlers/.gitignore b/packages/event-handlers/.gitignore new file mode 100644 index 000000000..9b1ee42e8 --- /dev/null +++ b/packages/event-handlers/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/event-handlers/README.md b/packages/event-handlers/README.md new file mode 100644 index 000000000..6afd1e675 --- /dev/null +++ b/packages/event-handlers/README.md @@ -0,0 +1,15 @@ +# @undb/command-handlers + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run src/index.ts +``` + +This project was created using `bun init` in bun v1.1.6. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/event-handlers/package.json b/packages/event-handlers/package.json new file mode 100644 index 000000000..0864f8592 --- /dev/null +++ b/packages/event-handlers/package.json @@ -0,0 +1,25 @@ +{ + "name": "@undb/event-handlers", + "module": "src/index.ts", + "types": "src/index.d.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@undb/base": "workspace:*", + "@undb/commands": "workspace:*", + "@undb/context": "workspace:*", + "@undb/cqrs": "workspace:*", + "@undb/dashboard": "workspace:*", + "@undb/di": "workspace:*", + "@undb/logger": "workspace:*", + "@undb/openapi": "workspace:*", + "@undb/template": "workspace:*", + "@undb/user": "workspace:*", + "ts-pattern": "^5.5.0" + } +} diff --git a/packages/event-handlers/src/handlers/dashboard-on-field-deleted.event-handler.ts b/packages/event-handlers/src/handlers/dashboard-on-field-deleted.event-handler.ts new file mode 100644 index 000000000..b9263a736 --- /dev/null +++ b/packages/event-handlers/src/handlers/dashboard-on-field-deleted.event-handler.ts @@ -0,0 +1,28 @@ +import { eventHandler } from "@undb/cqrs" +import { DashboardTableIdSpecification, injectDashboardRepository, type IDashboardRepository } from "@undb/dashboard" +import { singleton } from "@undb/di" +import type { IEventHandler } from "@undb/domain" +import { createLogger } from "@undb/logger" +import { FieldDeletedEvent } from "../../../table/src" + +@eventHandler(FieldDeletedEvent) +@singleton() +export class DashboardOnFieldDeletedEventHandle implements IEventHandler { + private readonly logger = createLogger(DashboardOnFieldDeletedEventHandle.name) + + constructor(@injectDashboardRepository() private readonly dashboardRepository: IDashboardRepository) {} + + async handle(event: FieldDeletedEvent): Promise { + this.logger.debug(event) + + const spec = new DashboardTableIdSpecification(event.payload.tableId) + const dashboards = await this.dashboardRepository.find(spec) + + for (const dashboard of dashboards) { + const spec = dashboard.$onFieldDeleted(event.payload.tableId, event.payload.field.id) + if (spec.isSome()) { + await this.dashboardRepository.updateOneById(dashboard, spec.unwrap()) + } + } + } +} diff --git a/packages/event-handlers/src/handlers/index.ts b/packages/event-handlers/src/handlers/index.ts new file mode 100644 index 000000000..005dfd3e2 --- /dev/null +++ b/packages/event-handlers/src/handlers/index.ts @@ -0,0 +1,3 @@ +import { DashboardOnFieldDeletedEventHandle } from "./dashboard-on-field-deleted.event-handler" + +export const eventHandlers = [DashboardOnFieldDeletedEventHandle] diff --git a/packages/event-handlers/src/index.ts b/packages/event-handlers/src/index.ts new file mode 100644 index 000000000..a5e0863ed --- /dev/null +++ b/packages/event-handlers/src/index.ts @@ -0,0 +1 @@ +export * from "./registry" diff --git a/packages/event-handlers/src/registry.ts b/packages/event-handlers/src/registry.ts new file mode 100644 index 000000000..6c6904f9c --- /dev/null +++ b/packages/event-handlers/src/registry.ts @@ -0,0 +1,8 @@ +import { EventBus } from "@undb/cqrs" +import { container } from "@undb/di" +import { eventHandlers } from "./handlers" + +export const registerEvents = () => { + const eventBus = container.resolve(EventBus) + eventBus.register(eventHandlers) +} diff --git a/packages/event-handlers/tsconfig.json b/packages/event-handlers/tsconfig.json new file mode 100644 index 000000000..6979fd21c --- /dev/null +++ b/packages/event-handlers/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} diff --git a/packages/persistence/package.json b/packages/persistence/package.json index e9cb9854a..e1ee7d1db 100644 --- a/packages/persistence/package.json +++ b/packages/persistence/package.json @@ -14,6 +14,7 @@ "@libsql/client": "^0.14.0", "@libsql/kysely-libsql": "^0.4.1", "@undb/audit": "workspace:*", + "@undb/cqrs": "workspace:*", "@undb/authz": "workspace:*", "@undb/base": "workspace:*", "@undb/context": "workspace:*", diff --git a/packages/persistence/src/dashboard/dashboard.filter-visitor.ts b/packages/persistence/src/dashboard/dashboard.filter-visitor.ts index ee2ef2822..d85b0705f 100644 --- a/packages/persistence/src/dashboard/dashboard.filter-visitor.ts +++ b/packages/persistence/src/dashboard/dashboard.filter-visitor.ts @@ -1,6 +1,7 @@ import type { Dashboard, DashboardBaseIdSpecification, + DashboardTableIdSpecification, IDashboardSpecVisitor, WithDashboardId, WithDashboardLayout, @@ -13,12 +14,25 @@ import type { DuplicatedDashboardSpecification } from "@undb/dashboard/src/speci import type { ExpressionBuilder } from "kysely" import { AbstractQBVisitor } from "../abstract-qb.visitor" import type { Database } from "../db" +import type { IQueryBuilder } from "../qb" export class DashboardFilterVisitor extends AbstractQBVisitor implements IDashboardSpecVisitor { - constructor(protected readonly eb: ExpressionBuilder) { + constructor( + protected readonly eb: ExpressionBuilder, + + private readonly qb: IQueryBuilder, + ) { super(eb) } + withDashboardTableId(v: DashboardTableIdSpecification): void { + const subQuery = this.qb + .selectFrom("undb_dashboard_table_id_mapping") + .select("dashboard_id") + .where("table_id", "=", v.tableId) + const cond = this.eb.eb("id", "in", subQuery) + this.addCond(cond) + } withDashboardLayout(v: WithDashboardLayout): void { throw new Error("Method not implemented.") } @@ -44,6 +58,6 @@ export class DashboardFilterVisitor extends AbstractQBVisitor impleme this.addCond(this.eb.eb("name", "like", `%${v.q}%`)) } clone(): this { - return new DashboardFilterVisitor(this.eb) as this + return new DashboardFilterVisitor(this.eb, this.qb) as this } } diff --git a/packages/persistence/src/dashboard/dashboard.mutate-visitor.ts b/packages/persistence/src/dashboard/dashboard.mutate-visitor.ts index 8be718dcd..c50be3a70 100644 --- a/packages/persistence/src/dashboard/dashboard.mutate-visitor.ts +++ b/packages/persistence/src/dashboard/dashboard.mutate-visitor.ts @@ -1,5 +1,7 @@ import type { + Dashboard, DashboardBaseIdSpecification, + DashboardTableIdSpecification, DuplicatedDashboardSpecification, IDashboardSpecVisitor, WithDashboardId, @@ -10,14 +12,32 @@ import type { WithDashboardWidgets, } from "@undb/dashboard" import { AbstractQBMutationVisitor } from "../abstract-qb.visitor" -import { json } from "../qb" +import { json, type IQueryBuilder } from "../qb" export class DashboardMutateVisitor extends AbstractQBMutationVisitor implements IDashboardSpecVisitor { + constructor( + private readonly dashboard: Dashboard, + private readonly qb: IQueryBuilder, + ) { + super() + } + + withDashboardTableId(v: DashboardTableIdSpecification): void { + throw new Error("Method not implemented.") + } withDashboardLayout(v: WithDashboardLayout): void { this.setData("layout", v.layout ? json(v.layout) : null) } withDashboardWidgets(v: WithDashboardWidgets): void { this.setData("widgets", v.widgets.value.length ? json(v.widgets.value) : null) + const tableIds = v.widgets.tableIds + const dashboardId = this.dashboard.id.value + const sql = this.qb + .insertInto("undb_dashboard_table_id_mapping") + .values(tableIds.map((tableId) => ({ dashboard_id: dashboardId, table_id: tableId }))) + .onConflict((ob) => ob.doNothing()) + .compile() + this.addSql(sql) } withDashboardBaseId(v: DashboardBaseIdSpecification): void { throw new Error("Method not implemented.") diff --git a/packages/persistence/src/dashboard/dashboard.query-repository.ts b/packages/persistence/src/dashboard/dashboard.query-repository.ts index 280f2288d..d743ad178 100644 --- a/packages/persistence/src/dashboard/dashboard.query-repository.ts +++ b/packages/persistence/src/dashboard/dashboard.query-repository.ts @@ -6,6 +6,7 @@ import { } from "@undb/dashboard" import { inject, singleton } from "@undb/di" import { None, Some, type Option } from "@undb/domain" +import { getCurrentTransaction } from "../ctx" import type { IQueryBuilder } from "../qb" import { injectQueryBuilder } from "../qb.provider" import { DashboardFilterVisitor } from "./dashboard.filter-visitor" @@ -21,11 +22,12 @@ export class DashboardQueryRepository implements IDashboardQueryRepository { ) {} async find(spec: Option): Promise { - const dashboards = await this.qb + const qb = getCurrentTransaction() ?? this.qb + const dashboards = await qb .selectFrom("undb_dashboard") .selectAll() .where((eb) => { - const visitor = new DashboardFilterVisitor(eb) + const visitor = new DashboardFilterVisitor(eb, qb) if (spec.isSome()) { spec.unwrap().accept(visitor) } @@ -39,11 +41,12 @@ export class DashboardQueryRepository implements IDashboardQueryRepository { async findOneById(id: string): Promise> { const spec = WithDashboardId.fromString(id) + const qb = getCurrentTransaction() ?? this.qb const dashboard = await this.qb .selectFrom("undb_dashboard") .selectAll() .where((eb) => { - const visitor = new DashboardFilterVisitor(eb) + const visitor = new DashboardFilterVisitor(eb, qb) spec.accept(visitor) return visitor.cond }) diff --git a/packages/persistence/src/dashboard/dashboard.repository.ts b/packages/persistence/src/dashboard/dashboard.repository.ts index d1f3a7229..f0a3323c1 100644 --- a/packages/persistence/src/dashboard/dashboard.repository.ts +++ b/packages/persistence/src/dashboard/dashboard.repository.ts @@ -36,7 +36,7 @@ export class DashboardRepository implements IDashboardRepository { .selectFrom("undb_dashboard") .selectAll() .where((eb) => { - const visitor = new DashboardFilterVisitor(eb) + const visitor = new DashboardFilterVisitor(eb, tx) spec.accept(visitor) return visitor.cond }) @@ -45,11 +45,12 @@ export class DashboardRepository implements IDashboardRepository { return dashboards.map((dashboard) => this.mapper.toDo(dashboard)) } async findOne(spec: IDashboardSpecification): Promise> { - const dashboard = await (getCurrentTransaction() ?? this.qb) + const tx = getCurrentTransaction() ?? this.qb + const dashboard = await tx .selectFrom("undb_dashboard") .selectAll() .where((eb) => { - const visitor = new DashboardFilterVisitor(eb) + const visitor = new DashboardFilterVisitor(eb, tx) spec.accept(visitor) return visitor.cond }) @@ -61,11 +62,12 @@ export class DashboardRepository implements IDashboardRepository { const spaceId = this.context.mustGetCurrentSpaceId() const spec = WithDashboardId.fromString(id).and(new WithDashboardSpaceId(spaceId)) - const dashboard = await (getCurrentTransaction() ?? this.qb) + const tx = getCurrentTransaction() ?? this.qb + const dashboard = await tx .selectFrom("undb_dashboard") .selectAll() .where((eb) => { - const visitor = new DashboardFilterVisitor(eb) + const visitor = new DashboardFilterVisitor(eb, tx) spec.accept(visitor) return visitor.cond }) @@ -77,7 +79,8 @@ export class DashboardRepository implements IDashboardRepository { const user = this.context.mustGetCurrentUserId() const values = this.mapper.toEntity(dashboard) - await getCurrentTransaction() + const qb = getCurrentTransaction() ?? this.qb + await qb .insertInto("undb_dashboard") .values({ ...values, @@ -87,6 +90,11 @@ export class DashboardRepository implements IDashboardRepository { updated_at: new Date().toISOString(), }) .execute() + const tableIds = dashboard.tableIds + await qb + .insertInto("undb_dashboard_table_id_mapping") + .values(tableIds.map((id) => ({ dashboard_id: dashboard.id.value, table_id: id }))) + .execute() await this.outboxService.save(dashboard) } @@ -99,10 +107,11 @@ export class DashboardRepository implements IDashboardRepository { async updateOneById(dashboard: Dashboard, spec: IDashboardSpecification): Promise { const userId = this.context.mustGetCurrentUserId() - const visitor = new DashboardMutateVisitor() + const qb = getCurrentTransaction() ?? this.qb + const visitor = new DashboardMutateVisitor(dashboard, qb) spec.accept(visitor) - await getCurrentTransaction() + await qb .updateTable("undb_dashboard") .set({ ...visitor.data, updated_by: userId, updated_at: new Date().toISOString() }) .where((eb) => eb.eb("id", "=", dashboard.id.value)) diff --git a/packages/persistence/src/db.ts b/packages/persistence/src/db.ts index e23bdc691..12cf09dcc 100644 --- a/packages/persistence/src/db.ts +++ b/packages/persistence/src/db.ts @@ -1,5 +1,5 @@ import type { Kyselify } from "drizzle-orm/kysely" -import type { Insertable,Selectable } from "kysely" +import type { Insertable, Selectable } from "kysely" import type { apiTokenTable, attachmentMapping, @@ -7,6 +7,7 @@ import type { audit, baseTable, dashboards, + dashboardTableIdMapping, emailVerificationCode, invitations, oauthAccount, @@ -26,6 +27,7 @@ import type { type SpaceTable = Kyselify type TableTable = Kyselify type DashboardTable = Kyselify +type DashboardTableIdMapping = Kyselify type TableIdMappingTable = Kyselify type RollupIdMappingTable = Kyselify type AuditTable = Kyselify @@ -50,6 +52,7 @@ export interface Database { undb_table: TableTable undb_dashboard: DashboardTable undb_audit: AuditTable + undb_dashboard_table_id_mapping: DashboardTableIdMapping undb_table_id_mapping: TableIdMappingTable undb_rollup_id_mapping: RollupIdMappingTable undb_outbox: OutboxTable diff --git a/packages/persistence/src/table/table.outbox-service.ts b/packages/persistence/src/table/table.outbox-service.ts index 437cc583e..3f6c3b9f5 100644 --- a/packages/persistence/src/table/table.outbox-service.ts +++ b/packages/persistence/src/table/table.outbox-service.ts @@ -1,5 +1,7 @@ import { injectContext, type IContext } from "@undb/context" -import { singleton } from "@undb/di" +import { EventBus } from "@undb/cqrs" +import { inject, singleton } from "@undb/di" +import type { IEventBus } from "@undb/domain" import type { ITableOutboxService, TableDo } from "@undb/table" import { getCurrentTransaction } from "../ctx" import { OutboxMapper } from "../outbox.mapper" @@ -9,12 +11,17 @@ export class TableOutboxService implements ITableOutboxService { constructor( @injectContext() private readonly context: IContext, + @inject(EventBus) + private readonly eventBus: IEventBus, ) {} async save(table: TableDo): Promise { const trx = getCurrentTransaction() const values = table.domainEvents.map((e) => OutboxMapper.fromEvent(e, this.context)) if (!values.length) return + await trx.insertInto("undb_outbox").values(values).execute() + this.eventBus.publishMany(table.domainEvents) + table.removeEvents(table.domainEvents) } @@ -22,7 +29,10 @@ export class TableOutboxService implements ITableOutboxService { const trx = getCurrentTransaction() const values = d.flatMap((table) => table.domainEvents.map((e) => OutboxMapper.fromEvent(e, this.context))) if (!values.length) return + await trx.insertInto("undb_outbox").values(values).execute() + this.eventBus.publishMany(d.flatMap((table) => table.domainEvents)) + d.forEach((table) => table.removeEvents(table.domainEvents)) } } diff --git a/packages/persistence/src/table/table.repository.ts b/packages/persistence/src/table/table.repository.ts index d934a7814..6d8a47518 100644 --- a/packages/persistence/src/table/table.repository.ts +++ b/packages/persistence/src/table/table.repository.ts @@ -1,7 +1,7 @@ -import { injectContext,type IContext } from "@undb/context" -import { executionContext,getCurrentSpaceId } from "@undb/context/server" -import { inject,singleton } from "@undb/di" -import { None,Option,Some } from "@undb/domain" +import { injectContext, type IContext } from "@undb/context" +import { executionContext, getCurrentSpaceId } from "@undb/context/server" +import { inject, singleton } from "@undb/di" +import { None, Option, Some } from "@undb/domain" import { TableComositeSpecification, TableIdSpecification, @@ -13,8 +13,8 @@ import { type TableId, } from "@undb/table" import { getCurrentTransaction } from "../ctx" -import type { InsertTable,InsertTableIdMapping } from "../db" -import { json,type IQueryBuilder } from "../qb" +import type { InsertTable, InsertTableIdMapping } from "../db" +import { json, type IQueryBuilder } from "../qb" import { injectQueryBuilder } from "../qb.provider" import { UnderlyingTableService } from "../underlying/underlying-table.service" import { TableDbQuerySpecHandler } from "./table-db.query-spec-handler" diff --git a/packages/persistence/src/tables.ts b/packages/persistence/src/tables.ts index 2f614f46f..f82b67a22 100644 --- a/packages/persistence/src/tables.ts +++ b/packages/persistence/src/tables.ts @@ -173,6 +173,23 @@ export const dashboards = sqliteTable( }, ) +export const dashboardTableIdMapping = sqliteTable( + "dashboard_table_id_mapping", + { + dashboardId: text("dashboard_id") + .notNull() + .references(() => dashboards.id), + tableId: text("table_id") + .notNull() + .references(() => tables.id), + }, + (table) => { + return { + pk: primaryKey({ columns: [table.dashboardId, table.tableId] }), + } + }, +) + export const attachments = sqliteTable( "attachment", { diff --git a/packages/space/src/events/space-created.event.ts b/packages/space/src/events/space-created.event.ts index a0c277c0e..0735aa631 100644 --- a/packages/space/src/events/space-created.event.ts +++ b/packages/space/src/events/space-created.event.ts @@ -13,7 +13,10 @@ export type ISpaceCreatedEventPayload = z.infer export class SpaceCreatedEvent extends BaseEvent { name = EVT_SPACE_CREATED - constructor(public readonly payload: ISpaceCreatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISpaceCreatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/space/src/events/space-updated.event.ts b/packages/space/src/events/space-updated.event.ts index 38571458b..a67b3e5b6 100644 --- a/packages/space/src/events/space-updated.event.ts +++ b/packages/space/src/events/space-updated.event.ts @@ -14,7 +14,10 @@ export type ISpaceUpdatedEventPayload = z.infer export class SpaceUpdatedEvent extends BaseEvent { name = EVT_SPACE_UPDATED - constructor(public readonly payload: ISpaceUpdatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISpaceUpdatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/field-created.event.ts b/packages/table/src/events/field-created.event.ts index b1231b84a..f8b80c6ee 100644 --- a/packages/table/src/events/field-created.event.ts +++ b/packages/table/src/events/field-created.event.ts @@ -17,7 +17,10 @@ export type IFieldCreatedEventPayload = z.infer export class FieldCreatedEvent extends BaseEvent { name = EVT_FIELD_CREATED - constructor(public readonly payload: IFieldCreatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IFieldCreatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/field-deleted.event.ts b/packages/table/src/events/field-deleted.event.ts index aa24a334e..27a8285b5 100644 --- a/packages/table/src/events/field-deleted.event.ts +++ b/packages/table/src/events/field-deleted.event.ts @@ -15,7 +15,10 @@ export type IFieldDeletedEventPayload = z.infer export class FieldDeletedEvent extends BaseEvent { name = EVT_FIELD_DELETED - constructor(public readonly payload: IFieldDeletedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IFieldDeletedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/field-updated.event.ts b/packages/table/src/events/field-updated.event.ts index ca4329208..fe58db757 100644 --- a/packages/table/src/events/field-updated.event.ts +++ b/packages/table/src/events/field-updated.event.ts @@ -16,7 +16,10 @@ export type IFieldUpdatedEventPayload = z.infer export class FieldUpdatedEvent extends BaseEvent { name = EVT_FIELD_UPDATED - constructor(public readonly payload: IFieldUpdatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IFieldUpdatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-table-form.event.ts b/packages/table/src/events/set-table-form.event.ts index d101c0397..09ba19d5c 100644 --- a/packages/table/src/events/set-table-form.event.ts +++ b/packages/table/src/events/set-table-form.event.ts @@ -16,7 +16,10 @@ export type ISetTableFormEventPayload = z.infer export class SetTableFormEvent extends BaseEvent { name = EVT_SET_TABLE_VIEW - constructor(public readonly payload: ISetTableFormEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetTableFormEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-table-rls.event.ts b/packages/table/src/events/set-table-rls.event.ts index 49da73eee..d8d964522 100644 --- a/packages/table/src/events/set-table-rls.event.ts +++ b/packages/table/src/events/set-table-rls.event.ts @@ -16,7 +16,10 @@ export type ISetTableRLSEventPayload = z.infer export class SetTableRLSEvent extends BaseEvent { name = EVT_SET_TABLE_RLS - constructor(public readonly payload: ISetTableRLSEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetTableRLSEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-aggregate.event.ts b/packages/table/src/events/set-view-aggregate.event.ts index c2ac62606..c1c00708f 100644 --- a/packages/table/src/events/set-view-aggregate.event.ts +++ b/packages/table/src/events/set-view-aggregate.event.ts @@ -17,7 +17,10 @@ export type ISetViewAggregateEventPayload = z.infer { name = EVT_SET_VIEW_COLOR - constructor(public readonly payload: ISetViewAggregateEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewAggregateEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-color.event.ts b/packages/table/src/events/set-view-color.event.ts index c43569771..9168fb1ff 100644 --- a/packages/table/src/events/set-view-color.event.ts +++ b/packages/table/src/events/set-view-color.event.ts @@ -18,7 +18,10 @@ export type ISetViewColorEventPayload = z.infer export class SetViewColorEvent extends BaseEvent { name = EVT_SET_VIEW_COLOR - constructor(public readonly payload: ISetViewColorEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewColorEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-fields.event.ts b/packages/table/src/events/set-view-fields.event.ts index f3f80f3d5..41f8e7344 100644 --- a/packages/table/src/events/set-view-fields.event.ts +++ b/packages/table/src/events/set-view-fields.event.ts @@ -18,7 +18,10 @@ export type ISetViewFieldsEventPayload = z.infer { name = EVT_SET_VIEW_FIELDS - constructor(public readonly payload: ISetViewFieldsEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewFieldsEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-filter.event.ts b/packages/table/src/events/set-view-filter.event.ts index b05a1d327..47842e6e5 100644 --- a/packages/table/src/events/set-view-filter.event.ts +++ b/packages/table/src/events/set-view-filter.event.ts @@ -17,7 +17,10 @@ export type ISetViewFilterEventPayload = z.infer { name = EVT_SET_VIEW_FILTER - constructor(public readonly payload: ISetViewFilterEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewFilterEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-option.event.ts b/packages/table/src/events/set-view-option.event.ts index 9a6dd4f4f..0f8ca9266 100644 --- a/packages/table/src/events/set-view-option.event.ts +++ b/packages/table/src/events/set-view-option.event.ts @@ -17,7 +17,10 @@ export type ISetViewOptionEventPayload = z.infer { name = EVT_SET_VIEW_OPTION - constructor(public readonly payload: ISetViewOptionEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewOptionEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/set-view-sort.event.ts b/packages/table/src/events/set-view-sort.event.ts index 72f291a0a..ddcdb9884 100644 --- a/packages/table/src/events/set-view-sort.event.ts +++ b/packages/table/src/events/set-view-sort.event.ts @@ -17,7 +17,10 @@ export type ISetViewSortEventPayload = z.infer export class SetViewSortEvent extends BaseEvent { name = EVT_SET_VIEW_SORT - constructor(public readonly payload: ISetViewSortEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ISetViewSortEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/table-created.event.ts b/packages/table/src/events/table-created.event.ts index d615de79d..42276d481 100644 --- a/packages/table/src/events/table-created.event.ts +++ b/packages/table/src/events/table-created.event.ts @@ -13,7 +13,10 @@ export type ITableCreatedEventPayload = z.infer export class TableCreatedEvent extends BaseEvent { name = EVT_TABLE_CREATED - constructor(public readonly payload: ITableCreatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: ITableCreatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/view-created.event.ts b/packages/table/src/events/view-created.event.ts index 76d0958db..bf74e537c 100644 --- a/packages/table/src/events/view-created.event.ts +++ b/packages/table/src/events/view-created.event.ts @@ -15,7 +15,10 @@ export type IViewCreatedEventPayload = z.infer export class ViewCreatedEvent extends BaseEvent { name = EVT_VIEW_CREATED - constructor(public readonly payload: IViewCreatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IViewCreatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/view-deleted.event.ts b/packages/table/src/events/view-deleted.event.ts index 90ed17e26..eaaf24312 100644 --- a/packages/table/src/events/view-deleted.event.ts +++ b/packages/table/src/events/view-deleted.event.ts @@ -15,7 +15,10 @@ export type IViewDeletedEventPayload = z.infer export class ViewDeletedEvent extends BaseEvent { name = EVT_VIEW_DELETED - constructor(public readonly payload: IViewDeletedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IViewDeletedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/events/view-updated.event.ts b/packages/table/src/events/view-updated.event.ts index 79fa8cf25..6049cef52 100644 --- a/packages/table/src/events/view-updated.event.ts +++ b/packages/table/src/events/view-updated.event.ts @@ -16,7 +16,10 @@ export type IViewUpdatedEventPayload = z.infer export class ViewUpdatedEvent extends BaseEvent { name = EVT_VIEW_UPDATED - constructor(public readonly payload: IViewUpdatedEventPayload) { - super(payload, undefined) + constructor( + public readonly payload: IViewUpdatedEventPayload, + spaceId: string, + ) { + super(payload, undefined, spaceId) } } diff --git a/packages/table/src/methods/create-field.method.ts b/packages/table/src/methods/create-field.method.ts index ead56a37e..72397d6fa 100644 --- a/packages/table/src/methods/create-field.method.ts +++ b/packages/table/src/methods/create-field.method.ts @@ -21,12 +21,15 @@ export function $createFieldSpec(this: TableDo, field: Field): Option v.name.value) applyRules(new ViewNameShouldBeUnique(names)) - const event = new ViewCreatedEvent({ - tableId: this.id.value, - view: view.toJSON(), - }) + const event = new ViewCreatedEvent( + { + tableId: this.id.value, + view: view.toJSON(), + }, + this.spaceId, + ) this.addDomainEvent(event) return { spec: Some(spec), view } diff --git a/packages/table/src/methods/delete-field.method.ts b/packages/table/src/methods/delete-field.method.ts index 337fe1394..20201e91c 100644 --- a/packages/table/src/methods/delete-field.method.ts +++ b/packages/table/src/methods/delete-field.method.ts @@ -23,10 +23,13 @@ export function deleteFieldMethod(this: TableDo, dto: IDeleteFieldDTO): [Field, spec.mutate(this) - const event = new FieldDeletedEvent({ - tableId: this.id.value, - field: field.toJSON(), - }) + const event = new FieldDeletedEvent( + { + tableId: this.id.value, + field: field.toJSON(), + }, + this.spaceId, + ) this.addDomainEvent(event) return [field, Some(spec)] diff --git a/packages/table/src/methods/delete-view.method.ts b/packages/table/src/methods/delete-view.method.ts index 546392eb2..e5d0c5ba2 100644 --- a/packages/table/src/methods/delete-view.method.ts +++ b/packages/table/src/methods/delete-view.method.ts @@ -15,10 +15,13 @@ export function deleteViewMethod(this: TableDo, dto: IDeleteViewDTO): Option v.name.value) applyRules(new ViewNameShouldBeUnique(names)) - const event = new ViewUpdatedEvent({ - tableId: this.id.value, - previous: spec.unwrap().previous.toJSON() ?? null, - view: view?.toJSON() ?? null, - }) + const event = new ViewUpdatedEvent( + { + tableId: this.id.value, + previous: spec.unwrap().previous.toJSON() ?? null, + view: view?.toJSON() ?? null, + }, + this.spaceId, + ) this.addDomainEvent(event) } diff --git a/packages/table/src/modules/views/view/variants/abstract-view.vo.ts b/packages/table/src/modules/views/view/variants/abstract-view.vo.ts index 5ec295c0b..65191dce3 100644 --- a/packages/table/src/modules/views/view/variants/abstract-view.vo.ts +++ b/packages/table/src/modules/views/view/variants/abstract-view.vo.ts @@ -269,7 +269,7 @@ export abstract class AbstractView { if (this.widgets.isSome()) { const previous = this.widgets.into(null) const widgets = this.widgets.unwrap().map((w) => { - const updated = w.deleteField(field) + const updated = w.deleteField(field.id.value) return updated.isSome() ? updated.unwrap() : w }) diff --git a/packages/table/src/modules/views/view/view-fields/view-fields.vo.ts b/packages/table/src/modules/views/view/view-fields/view-fields.vo.ts index 68c9a293b..efe3efcfe 100644 --- a/packages/table/src/modules/views/view/view-fields/view-fields.vo.ts +++ b/packages/table/src/modules/views/view/view-fields/view-fields.vo.ts @@ -15,16 +15,26 @@ export type IViewFields = z.infer export class ViewFields extends ValueObject { constructor(table: TableDo, props: IViewFields) { - const fields = table.schema.fields.map((field) => { - const exists = props.find((f) => f.fieldId === field.id.value) - if (exists) { - return exists + const fields = props.map((prop) => { + const field = table.schema.getFieldByIdOrName(prop.fieldId) + if (field) { + return prop } return { - fieldId: field.id.value, - hidden: false, + fieldId: prop.fieldId, + hidden: true, } }) + + table.schema.fields.forEach((field) => { + if (!fields.some((f) => f.fieldId === field.id.value)) { + fields.push({ + fieldId: field.id.value, + hidden: false, + }) + } + }) + super(fields) } diff --git a/packages/table/src/modules/widgets/widget.vo.ts b/packages/table/src/modules/widgets/widget.vo.ts index b903bb9fb..855260a39 100644 --- a/packages/table/src/modules/widgets/widget.vo.ts +++ b/packages/table/src/modules/widgets/widget.vo.ts @@ -3,7 +3,6 @@ import { z } from "@undb/zod" import { tableId } from "../../table-id.vo" import { aggregate } from "../aggregate/aggregate.vo" import { chart } from "../chart/chart.vo" -import type { Field } from "../schema/fields/field.type" import { widgetId, WidgetIdVo } from "./widget-id.vo" import { widgetName } from "./widget-name.vo" @@ -61,6 +60,11 @@ export class WidgetVO extends ValueObject { }, }) } + + static fromJSON(dto: IWidgetDTO) { + return new this(dto) + } + public get id() { return this.props.id } @@ -85,10 +89,10 @@ export class WidgetVO extends ValueObject { return isValidWidget(this.toJSON()) } - deleteField(field: Field): Option { + deleteField(fieldId: string): Option { if (this.props.item.type === "aggregate") { if (this.props.item.aggregate.type !== "count") { - if (this.props.item.aggregate.config.field === field.id.value) { + if (this.props.item.aggregate.config.field === fieldId) { const widget = new WidgetVO({ ...this.props, item: { diff --git a/packages/table/src/table.do.ts b/packages/table/src/table.do.ts index b855cdbc9..06996110e 100644 --- a/packages/table/src/table.do.ts +++ b/packages/table/src/table.do.ts @@ -95,7 +95,7 @@ export class TableDo extends AggregateRoot { return form.visibleFields.map((f) => this.schema.getFieldById(new FieldIdVo(f.fieldId)).unwrap()) } } - const showSystemFields = this.views.getViewById(viewId).showSystemFields + const showSystemFields = !!this.views?.getViewById(viewId).showSystemFields return fields.filter((f) => (showSystemFields ? true : !f.isSystem)) }