From 9d436833902795ad46531a2a74c1ad63529af40b Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Thu, 9 Nov 2023 02:18:56 +0900 Subject: [PATCH] New tag `@minItems 1` Until yesterday, `prisma-markdown` could not distinguish whether 1: N relationships are mandatory or not. In such reason, `prisma-markdown` had drawn 1: N relationship as `||---|{` symbol regardless of the N position is whether mandatory or optional. To make it clear, I've come with new idea `@minItems 1` tag. If the tag being written on a 1: N relationship accessor, `prisma-markdown` will draw `||---|{` symbol. Otherwise it does not exist, `||---o{` symbol would be drawn instead. ```typescript model bbs_articles { id String @id @db.Uuid created_at DateTime @db.Timestamptz deleted_at DateTime? @db.Timestamptz //---- // RELATIONS //---- /// @minItems 1 snapshots bbs_article_snapshots[] comments bbs_article_comments[] } ``` --- ERD.md | 96 +-- README.md | 10 +- package.json | 2 +- schema.prisma | 28 +- src/writers/MermaidWriter.ts | 24 +- test/drive.md | 645 ++++++++++++++++++++ test/drive.prisma | 1074 ++++++++++++++++++++++++++++++++++ test/implicit.md | 10 +- test/summary.md | 10 +- 9 files changed, 1830 insertions(+), 69 deletions(-) create mode 100644 test/drive.md create mode 100644 test/drive.prisma diff --git a/ERD.md b/ERD.md index 362f0ec..0920907 100644 --- a/ERD.md +++ b/ERD.md @@ -61,13 +61,13 @@ erDiagram Int sequence } "bbs_article_snapshots" }|--|| "bbs_articles" : article -"bbs_article_snapshot_files" }|--|| "bbs_article_snapshots" : snapshot -"bbs_article_snapshot_files" }|--|| "attachment_files" : file -"bbs_article_comments" }|--|| "bbs_articles" : article +"bbs_article_snapshot_files" }o--|| "bbs_article_snapshots" : snapshot +"bbs_article_snapshot_files" }o--|| "attachment_files" : file +"bbs_article_comments" }o--|| "bbs_articles" : article "bbs_article_comments" }o--o| "bbs_article_comments" : parent "bbs_article_comment_snapshots" }|--|| "bbs_article_comments" : comment -"bbs_article_comment_snapshot_files" }|--|| "bbs_article_comment_snapshots" : snapshot -"bbs_article_comment_snapshot_files" }|--|| "attachment_files" : file +"bbs_article_comment_snapshot_files" }o--|| "bbs_article_comment_snapshots" : snapshot +"bbs_article_comment_snapshot_files" }o--|| "attachment_files" : file ``` ### `attachment_files` @@ -284,14 +284,14 @@ erDiagram String shopping_sale_snapshot_channel_id FK String shopping_channel_category_id FK } -"shopping_channel_categories" }|--|| "shopping_channels" : channel +"shopping_channel_categories" }o--|| "shopping_channels" : channel "shopping_channel_categories" }o--o| "shopping_channel_categories" : parent -"shopping_sales" }|--|| "shopping_sections" : section +"shopping_sales" }o--|| "shopping_sections" : section "shopping_sale_snapshots" }|--|| "shopping_sales" : sale -"shopping_sale_snapshot_channels" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_channels" }|--|| "shopping_channels" : channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_sale_snapshot_channels" : to_channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_channel_categories" : category +"shopping_sale_snapshot_channels" }o--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_channels" }o--|| "shopping_channels" : channel +"shopping_sale_snapshot_channel_categories" }o--|| "shopping_sale_snapshot_channels" : to_channel +"shopping_sale_snapshot_channel_categories" }o--|| "shopping_channel_categories" : category ``` ### `shopping_channels` @@ -460,7 +460,7 @@ erDiagram "shopping_customers" }o--|| "shopping_external_users" : external_user "shopping_customers" }o--|| "shopping_citizens" : citizen "shopping_members" }o--|| "shopping_citizens" : citizen -"shopping_member_emails" }|--|| "shopping_members" : member +"shopping_member_emails" }o--|| "shopping_members" : member "shopping_sellers" |o--|| "shopping_members" : member "shopping_administrators" |o--|| "shopping_members" : member ``` @@ -807,19 +807,19 @@ erDiagram DateTime updated_at DateTime deleted_at "nullable" } -"shopping_sales" }|--|| "shopping_sections" : section +"shopping_sales" }o--|| "shopping_sections" : section "shopping_sale_snapshots" }|--|| "shopping_sales" : sale -"shopping_sale_snapshot_channels" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_channels" }|--|| "shopping_channels" : channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_sale_snapshot_channels" : to_channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_channel_categories" : category +"shopping_sale_snapshot_channels" }o--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_channels" }o--|| "shopping_channels" : channel +"shopping_sale_snapshot_channel_categories" }o--|| "shopping_sale_snapshot_channels" : to_channel +"shopping_sale_snapshot_channel_categories" }o--|| "shopping_channel_categories" : category "shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_sale_snapshot_unit_options" }o--|| "shopping_sale_snapshot_units" : unit +"shopping_sale_snapshot_unit_option_candidates" }o--|| "shopping_sale_snapshot_unit_options" : option "shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_stocks" : stock -"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_option_candidates" : candidate -"shopping_channel_categories" }|--|| "shopping_channels" : channel +"shopping_sale_snapshot_unit_stock_choices" }o--|| "shopping_sale_snapshot_unit_stocks" : stock +"shopping_sale_snapshot_unit_stock_choices" }o--|| "shopping_sale_snapshot_unit_option_candidates" : candidate +"shopping_channel_categories" }o--|| "shopping_channels" : channel "shopping_channel_categories" }o--o| "shopping_channel_categories" : parent ``` @@ -1256,17 +1256,17 @@ erDiagram Int quantity Int sequence } -"shopping_cart_commodities" }|--|| "shopping_carts" : cart -"shopping_cart_commodities" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_cart_commodities" }o--|| "shopping_carts" : cart +"shopping_cart_commodities" }o--|| "shopping_sale_snapshots" : snapshot "shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity -"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_unit_stocks" : stock -"shopping_cart_commodity_stock_choices" }|--|| "shopping_cart_commodity_stocks" : stock -"shopping_cart_commodity_stock_choices" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_cart_commodity_stocks" }o--|| "shopping_sale_snapshot_units" : unit +"shopping_cart_commodity_stocks" }o--|| "shopping_sale_snapshot_unit_stocks" : stock +"shopping_cart_commodity_stock_choices" }o--|| "shopping_cart_commodity_stocks" : stock +"shopping_cart_commodity_stock_choices" }o--|| "shopping_sale_snapshot_unit_options" : option "shopping_cart_commodity_stock_choices" }o--|| "shopping_sale_snapshot_unit_option_candidates" : candidate "shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_sale_snapshot_unit_options" }o--|| "shopping_sale_snapshot_units" : unit +"shopping_sale_snapshot_unit_option_candidates" }o--|| "shopping_sale_snapshot_unit_options" : option "shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit ``` @@ -1482,12 +1482,12 @@ erDiagram } "shopping_orders" }o--|| "shopping_addresses" : address "shopping_order_goods" }|--|| "shopping_orders" : order -"shopping_order_goods" }|--|| "shopping_cart_commodities" : commodity +"shopping_order_goods" }o--|| "shopping_cart_commodities" : commodity "shopping_order_publishes" |o--|| "shopping_orders" : order "shopping_delivery_pieces" }|--|| "shopping_deliveries" : delivery -"shopping_delivery_pieces" }|--|| "shopping_order_goods" : good -"shopping_delivery_pieces" }|--|| "shopping_cart_commodity_stocks" : cart_commodity_stock -"shopping_delivery_journeys" }|--|| "shopping_deliveries" : delivery +"shopping_delivery_pieces" }o--|| "shopping_order_goods" : good +"shopping_delivery_pieces" }o--|| "shopping_cart_commodity_stocks" : cart_commodity_stock +"shopping_delivery_journeys" }o--|| "shopping_deliveries" : delivery "shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity ``` @@ -1773,16 +1773,16 @@ erDiagram DateTime created_at DateTime expired_at "nullable" } -"shopping_coupon_criterias" }|--|| "shopping_coupons" : coupon +"shopping_coupon_criterias" }o--|| "shopping_coupons" : coupon "shopping_coupon_section_criterias" |o--|| "shopping_coupon_criterias" : base "shopping_coupon_channel_criterias" |o--|| "shopping_coupon_criterias" : base "shopping_coupon_seller_criterias" |o--|| "shopping_coupon_criterias" : base "shopping_coupon_sale_criterias" |o--|| "shopping_coupon_criterias" : base "shopping_coupon_funnel_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_tickets" }|--|| "shopping_coupons" : coupon +"shopping_coupon_tickets" }o--|| "shopping_coupons" : coupon "shopping_coupon_tickets" |o--|| "shopping_coupon_disposables" : disposable "shopping_coupon_ticket_payments" |o--|| "shopping_coupon_tickets" : ticket -"shopping_coupon_disposables" }|--|| "shopping_coupons" : coupon +"shopping_coupon_disposables" }o--|| "shopping_coupons" : coupon ``` ### `shopping_coupons` @@ -2192,12 +2192,12 @@ erDiagram DateTime created_at DateTime deleted_at "nullable" } -"shopping_deposit_histories" }|--|| "shopping_deposits" : deposit -"shopping_deposit_histories" }|--|| "shopping_citizens" : citizen -"shopping_deposit_charges" }|--|| "shopping_customers" : customer +"shopping_deposit_histories" }o--|| "shopping_deposits" : deposit +"shopping_deposit_histories" }o--|| "shopping_citizens" : citizen +"shopping_deposit_charges" }o--|| "shopping_customers" : customer "shopping_deposit_charge_publishes" |o--|| "shopping_deposit_charges" : charge -"shopping_mileage_histories" }|--|| "shopping_mileages" : mileage -"shopping_mileage_histories" }|--|| "shopping_citizens" : citizen +"shopping_mileage_histories" }o--|| "shopping_mileages" : mileage +"shopping_mileage_histories" }o--|| "shopping_citizens" : citizen "shopping_customers" }o--|| "shopping_citizens" : citizen ``` @@ -2421,16 +2421,16 @@ erDiagram String shopping_sale_id FK DateTime created_at } -"shopping_sale_snapshot_inquiries" ||--|| "bbs_articles" : base -"shopping_sale_snapshot_inquiries" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_inquiries" |o--|| "bbs_articles" : base +"shopping_sale_snapshot_inquiries" }o--|| "shopping_sale_snapshots" : snapshot "shopping_sale_snapshot_questions" |o--|| "shopping_sale_snapshot_inquiries" : base "shopping_sale_snapshot_reviews" |o--|| "shopping_sale_snapshot_inquiries" : base -"shopping_sale_snapshot_review_snapshots" ||--|| "bbs_article_snapshots" : base -"shopping_sale_snapshot_inquiry_answers" ||--|| "bbs_articles" : base +"shopping_sale_snapshot_review_snapshots" |o--|| "bbs_article_snapshots" : base +"shopping_sale_snapshot_inquiry_answers" |o--|| "bbs_articles" : base "shopping_sale_snapshot_inquiry_answers" |o--|| "shopping_sale_snapshot_inquiries" : inquiry -"shopping_sale_snapshot_inquiry_comments" ||--|| "bbs_article_comments" : base +"shopping_sale_snapshot_inquiry_comments" |o--|| "bbs_article_comments" : base "bbs_article_snapshots" }|--|| "bbs_articles" : article -"bbs_article_comments" }|--|| "bbs_articles" : article +"bbs_article_comments" }o--|| "bbs_articles" : article "bbs_article_comments" }o--o| "bbs_article_comments" : parent ``` diff --git a/README.md b/README.md index 328011e..e7351cf 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Also, if you use `@erd ` instead of `@namespace `, target model woul - `@erd `: Only ERD - `@describe `: Only markdown content - `@hidden`: Neither ERD nor markdown content + - `@minItems 1`: Mandatory relationship when 1: **N** (`||---|{`) ```prisma /// Both description and ERD on Actors chatper. @@ -67,7 +68,14 @@ Also, if you use `@erd ` instead of `@namespace `, target model woul /// @namespace Actors /// @erd Articles /// @erd Orders -model shopping_customers {} +model shopping_customers { + /// The tag "minItems 1" means mandatory relationship `||---|{`. + /// + /// Otherwise, no tag means optional relationship `||---o{`. + /// + /// @minItems 1 + login_histories shopping_customer_login_histories[] +} /// Only description on Actors chapter. /// diff --git a/package.json b/package.json index 047af58..e8ce2aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prisma-markdown", - "version": "1.0.6", + "version": "1.0.7", "description": "Prisma Markdown documents generator including ERD diagrams and comment descriptions", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/schema.prisma b/schema.prisma index c52ca65..1336b9f 100644 --- a/schema.prisma +++ b/schema.prisma @@ -1026,7 +1026,7 @@ model shopping_sellers { deleted_at DateTime? @db.Timestamptz //---- - // RELATIONSHIPS + // RELATIONS //---- member shopping_members @relation(fields: [shopping_member_id], references: [id], onDelete: Cascade) shopping_order_goods shopping_order_goods[] @@ -1223,9 +1223,11 @@ model shopping_sales { //---- // RELATIONS //---- - section shopping_sections @relation(fields: [shopping_section_id], references: [id], onDelete: Cascade) - sellerCustomer shopping_customers @relation(fields: [shopping_seller_customer_id], references: [id], onDelete: Cascade) - snapshots shopping_sale_snapshots[] + section shopping_sections @relation(fields: [shopping_section_id], references: [id], onDelete: Cascade) + sellerCustomer shopping_customers @relation(fields: [shopping_seller_customer_id], references: [id], onDelete: Cascade) + + /// @minItems 1 + snapshots shopping_sale_snapshots[] inquiries shopping_sale_snapshot_inquiries[] shopping_coupon_sale_criterias shopping_coupon_sale_criterias[] @@ -1280,8 +1282,10 @@ model shopping_sale_snapshots { content shopping_sale_snapshot_contents? to_channels shopping_sale_snapshot_channels[] - units shopping_sale_snapshot_units[] - tags shopping_sale_snapshot_tags[] + + /// @minItems 1 + units shopping_sale_snapshot_units[] + tags shopping_sale_snapshot_tags[] shopping_cart_commodities shopping_cart_commodities[] shopping_sale_snapshot_inquiries shopping_sale_snapshot_inquiries[] @@ -1542,6 +1546,8 @@ model shopping_sale_snapshot_units { options shopping_sale_snapshot_unit_options[] /// List of stocks. + /// + /// @minItems 1 stocks shopping_sale_snapshot_unit_stocks[] shopping_cart_commodity_stocks shopping_cart_commodity_stocks[] @@ -2008,6 +2014,8 @@ model shopping_cart_commodities { snapshot shopping_sale_snapshots @relation(fields: [shopping_sale_snapshot_id], references: [id], onDelete: Cascade) /// List of wrapper of final stocks. + /// + /// @minItems 1 stocks shopping_cart_commodity_stocks[] /// List of order goods for purchase. @@ -2249,6 +2257,8 @@ model shopping_orders { publish shopping_order_publishes? /// List of goods to purchase. + /// + /// @minItems 1 goods shopping_order_goods[] /// List of payed tickets for discount @@ -2471,10 +2481,12 @@ model shopping_deliveries { //---- sellerCustomer shopping_customers @relation(fields: [shopping_seller_customer_id], references: [id], onDelete: Cascade) journeys shopping_delivery_journeys[] - pieces shopping_delivery_pieces[] + + /// @minItems 1 + pieces shopping_delivery_pieces[] + // Considering principles, it must be UK. // However, some seller takes a mistake, so it can't be. - @@index([shopping_seller_customer_id, created_at]) @@index([invoice_code]) @@index([created_at]) diff --git a/src/writers/MermaidWriter.ts b/src/writers/MermaidWriter.ts index ce0aed0..1d25c39 100644 --- a/src/writers/MermaidWriter.ts +++ b/src/writers/MermaidWriter.ts @@ -1,4 +1,5 @@ import { DMMF } from "@prisma/generator-helper"; +import { PrismaUtil } from "../utils/PrismaUtil"; export namespace MermaidWriter { export const write = (chapter: DMMF.Model[]) => @@ -80,7 +81,9 @@ export namespace MermaidWriter { ), )) ? "o" - : "|", + : isMandatoryMany({ model: props.model, field, target }) + ? "|" + : "o", "--", props.model === target ? "o" : "|", "|", @@ -93,4 +96,23 @@ export namespace MermaidWriter { field.name, ].join(" "); }; + + const isMandatoryMany = (props: { + target: DMMF.Model; + model: DMMF.Model; + field: DMMF.Field; + }): boolean => { + const opposite = props.target.fields.find( + (f) => + f.relationName === props.field.relationName && + f.type === props.model.name, + ); + if (opposite === undefined) return false; + + const values: string[] = PrismaUtil.tagValues("minItems")(opposite); + if (values.length === 0) return false; + + const numeric: number = Number(values[0]); + return !isNaN(numeric) && numeric >= 1; + }; } diff --git a/test/drive.md b/test/drive.md new file mode 100644 index 0000000..1ee20c2 --- /dev/null +++ b/test/drive.md @@ -0,0 +1,645 @@ +# Virtual Drive System +> Generated by [`prisma-markdown`](https://github.com/samchon/prisma-markdown) + +- [Articles](#Articles) +- [Actors](#Actors) +- [Repositories](#Repositories) +- [Issues](#Issues) + +## Articles +```mermaid +erDiagram +"attachment_files" { + String id PK + String name "nullable" + String extension "nullable" + String url + DateTime created_at +} +"bbs_articles" { + String id PK + DateTime created_at + DateTime deleted_at "nullable" +} +"bbs_article_snapshots" { + String id PK + String bbs_article_id FK + String format + String title + String body + DateTime created_at +} +"bbs_article_snapshot_files" { + String id PK + String bbs_article_snapshot_id FK + String attachment_file_id FK + Int sequence +} +"bbs_article_comments" { + String id PK + String bbs_article_id FK + String parent_id FK "nullable" + DateTime created_at + DateTime deleted_at "nullable" +} +"bbs_article_comment_snapshots" { + String id PK + String bbs_article_comment_id FK + String format + String body + DateTime created_at +} +"bbs_article_comment_snapshot_files" { + String id PK + String bbs_article_comment_snapshot_id FK + String attachment_file_id FK + Int sequence +} +"bbs_article_snapshots" }|--|| "bbs_articles" : article +"bbs_article_snapshot_files" }o--|| "bbs_article_snapshots" : snapshot +"bbs_article_snapshot_files" }o--|| "attachment_files" : file +"bbs_article_comments" }o--|| "bbs_articles" : article +"bbs_article_comments" }o--o| "bbs_article_comments" : parent +"bbs_article_comment_snapshots" }|--|| "bbs_article_comments" : comment +"bbs_article_comment_snapshot_files" }o--|| "bbs_article_comment_snapshots" : snapshot +"bbs_article_comment_snapshot_files" }o--|| "attachment_files" : file +``` + +### `attachment_files` +Attachment File. + +Every attachment files that are managed in this drive mall system. + +For reference, it is possible to omit one of file name or extension like +`.gitignore` or `README` case, but not possible to omit both of them, + +**Properties** + - `id`: Primary Key. + - `name` + > File name, except extension. + > + > Possible to omit like `.gitignore` case. + - `extension` + > Extension. + > + > Possible to omit like `README` case. + - `url`: URL path of the real file. + - `created_at`: Creation time of record. + +### `bbs_articles` +Article entity. + +`bbs_articles` is a super-type entity of all kinds of articles in the +current drive mall system, literally shaping individual articles of +the bulletin board. + +And, as you can see, the elements that must inevitably exist in the +article, such as the title or the body, do not exist in the `bbs_articles`, +but exist in the subsidiary entity, [bbs_article_snapshots](#bbs_article_snapshots), as a +1: N relationship, which is because a new snapshot record is published +every time the article is modified. + +The reason why a new snapshot record is published every time the article +is modified is to preserve the evidence. Due to the nature of e-community, +there is always a threat of dispute among the participants. And it can +happen that disputes arise through articles or comments, and to prevent +such things as modifying existing articles to manipulate the situation, +the article is designed in this structure. + +In other words, to keep evidence, and prevent fraud. + +**Properties** + - `id`: Primary Key. + - `created_at`: Creation time of article. + - `deleted_at` + > Deletion time of article. + > + > To keep evidence, do not delete the article, but just mark it as + > deleted. + +### `bbs_article_snapshots` +Snapshot of article. + +`bbs_article_snapshots` is a snapshot entity that contains the contents of +the article, as mentioned in [bbs_articles](#bbs_articles), the contents of the +article are separated from the article record to keep evidence and prevent +fraud. + +**Properties** + - `id`: Primary Key. + - `bbs_article_id`: Belong article's [bbs_articles.id](#bbs_articles) + - `format` + > Format of body. + > + > Same meaning with extension like `html`, `md`, `txt`. + - `title`: Title of article. + - `body`: Content body of article. + - `created_at` + > Creation time of record. + > + > It means creation time or update time or article. + +### `bbs_article_snapshot_files` +Attachment file of article snapshot. + +`bbs_article_snapshot_files` is an entity that shapes the attached files of +the article snapshot. + +`bbs_article_snapshot_files` is a typical pair relationship table to +resolve the M: N relationship between [bbs_article_snapshots](#bbs_article_snapshots) and +[attachment_files](#attachment_files) tables. Also, to ensure the order of the attached +files, it has an additional `sequence` attribute, which we will continue to +see in this documents. + +**Properties** + - `id`: Primary Key. + - `bbs_article_snapshot_id`: Belonged snapshot's [bbs_article_snapshots.id](#bbs_article_snapshots) + - `attachment_file_id`: Belonged file's [attachment_files.id](#attachment_files) + - `sequence`: Sequence of attachment file in the snapshot. + +### `bbs_article_comments` +Comment written on an article. + +`bbs_article_comments` is an entity that shapes the comments written on an +article. + +And for this comment, as in the previous relationship between +[bbs_articles](#bbs_articles) and [bbs_article_snapshots](#bbs_article_snapshots), the content body +of the comment is stored in the sub [bbs_article_comment_snapshots](#bbs_article_comment_snapshots) +table for evidentialism, and a new snapshot record is issued every time +the comment is modified. + +Also, `bbs_article_comments} is expressing the relationship of the +hierarchical reply structure through the `parent_id` attribute. + +**Properties** + - `id`: Primary Key. + - `bbs_article_id`: Belonged article's [bbs_articles.id](#bbs_articles) + - `parent_id` + > Parent comment's [bbs_article_comments.id](#bbs_article_comments) + > + > Used to express the hierarchical reply structure. + - `created_at`: Creation time of comment. + - `deleted_at` + > Deletion time of comment. + > + > Do not allow to delete the comment, but just mark it as deleted, + > to keep evidence. + +### `bbs_article_comment_snapshots` +Snapshot of comment. + +`bbs_article_comment_snapshots` is a snapshot entity that contains the +contents of the comment. + +As mentioned in [bbs_article_comments](#bbs_article_comments), designed to keep evidence +and prevent fraud. + +**Properties** + - `id`: Primary Key. + - `bbs_article_comment_id`: Belonged article's [bbs_article_comments.id](#bbs_article_comments) + - `format` + > Format of content body. + > + > Same meaning with extension like `html`, `md`, `txt`. + - `body`: Content body of comment. + - `created_at` + > Creation time of record. + > + > It means creation time or update time or comment. + +### `bbs_article_comment_snapshot_files` +Attachment file of comment snapshot. + +`bbs_article_comment_snapshot_files` is an entity resolving the M:N +relationship between [bbs_article_comment_snapshots](#bbs_article_comment_snapshots) and +[attachment_files](#attachment_files) tables. + +**Properties** + - `id`: Primary Key. + - `bbs_article_comment_snapshot_id`: Belonged snapshot's [bbs_article_comment_snapshots.id](#bbs_article_comment_snapshots) + - `attachment_file_id`: Belonged file's [attachment_files.id](#attachment_files) + - `sequence` + > Sequence order. + > + > Sequence order of the attached file in the belonged snapshot. + + +## Actors +```mermaid +erDiagram +"drive_customers" { + String id PK + String drive_member_id FK "nullable" + String drive_external_user_id FK "nullable" + String drive_citizen_id FK "nullable" + String href + String referrer + String ip + DateTime created_at +} +"drive_external_users" { + String id PK + String application + String uid + String nickname + String data "nullable" + String password + DateTime created_at +} +"drive_citizens" { + String id PK + String mobile + String name + DateTime created_at + DateTime deleted_at "nullable" +} +"drive_members" { + String id PK + String drive_account_id FK "nullable" + String drive_citizen_id FK "nullable" + String nickname + String password + DateTime created_at + DateTime updated_at + DateTime withdrawn_at "nullable" +} +"drive_member_emails" { + String id PK + String drive_member_id FK + String value + DateTime created_at +} +"drive_accounts" { + String id PK + String code UK + DateTime created_at + DateTime deleted_at "nullable" +} +"drive_enterprises" { + String id PK + String drive_account_id FK + String name + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_enterprise_employees" { + String id PK + String drive_enterprise_id FK + String drive_member_id FK + String role + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_enterprise_employee_appointments" { + String id PK + String drive_enterprise_employee_id FK + String drive_customer_id FK + String role + DateTime created_at +} +"drive_enterprise_teams" { + String id PK + String drive_enterprise_id FK + String name + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_enterprise_team_companions" { + String id PK + String drive_enterprise_team_id FK + String drive_enterprise_employee_id FK + String role + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_enterprise_team_companion_appointments" { + String id PK + String drive_enterprise_team_companion_id FK + String drive_customer_id FK + String role + DateTime created_at +} +"drive_customers" }o--|| "drive_members" : member +"drive_customers" }o--|| "drive_external_users" : external_user +"drive_customers" }o--|| "drive_citizens" : citizen +"drive_members" }o--|| "drive_accounts" : account +"drive_members" }o--|| "drive_citizens" : citizen +"drive_member_emails" }o--|| "drive_members" : member +"drive_enterprises" }o--|| "drive_accounts" : account +"drive_enterprise_employees" }o--|| "drive_enterprises" : enterprise +"drive_enterprise_employees" }o--|| "drive_members" : member +"drive_enterprise_employee_appointments" }o--|| "drive_enterprise_employees" : employee +"drive_enterprise_employee_appointments" }o--|| "drive_customers" : customer +"drive_enterprise_teams" }o--|| "drive_enterprises" : enterprise +"drive_enterprise_team_companions" }o--|| "drive_enterprise_teams" : team +"drive_enterprise_team_companions" }o--|| "drive_enterprise_employees" : employee +"drive_enterprise_team_companion_appointments" }o--|| "drive_enterprise_team_companions" : companion +"drive_enterprise_team_companion_appointments" }o--|| "drive_customers" : customer +``` + +### `drive_customers` + +**Properties** + - `id`: Primary Key. + - `drive_member_id`: Belonged member's [drive_members.id](#drive_members) + - `drive_external_user_id`: Belonged external service user's [drive_external_users.id](#drive_external_users) + - `drive_citizen_id`: Belonged citizen's [drive_citizens.id](#drive_citizens) + - `href` + > Connection URL. + > + > [window.location.href](#window) + - `referrer` + > Referrer URL. + > + > [window.document.referrer](#window) + - `ip`: IP address, + - `created_at` + > Creation time of record. + > + > It means the time when the customer connected to the drive mall. + +### `drive_external_users` + +**Properties** + - `id`: Primary Key. + - `application` + > Identifier code of the external service. + > + > It can be same with [drive_channels.code](#drive_channels) in common. + - `uid`: Identifier key of external user from the external system. + - `nickname`: Nickname of external user in the external system. + - `data`: Additional information about external user from the external system. + - `password` + > Password of external user from the external system. + > + > This is a password issued to the user by an external service, and is + > by no means the actual user password. However, for customers who + > entered the same application and code as the current external system + > user, this is to determine whether to view this as a correct external + > system user or a violation. + - `created_at` + > Creation time of record. + > + > Another word, first time when the external user connected. + +### `drive_citizens` + +**Properties** + - `id`: Primary Key. + - `mobile`: Mobile phone number. + - `name`: Real name, or equivalent name identifiable. + - `created_at` + > Creation time of record. + > + > In other words, the 1st time of citizen activation. + - `deleted_at`: Deletion time of record. + +### `drive_members` + +**Properties** + - `id`: Primary Key. + - `drive_account_id`: Belonged account's [drive_accounts.id](#drive_accounts) + - `drive_citizen_id`: Belonged citizen's [drive_citizens.id](#drive_citizens) + - `nickname`: Nickname. + - `password`: Password for log-in. + - `created_at` + > Creation time of record. + > + > In other words, the joining time. + - `updated_at`: Update time of record. + - `withdrawn_at`: Deletion time of record. + +### `drive_member_emails` + +**Properties** + - `id`: + - `drive_member_id`: Belonged member's [drive_members.id](#drive_members) + - `value`: Email address. + - `created_at`: Creation time of record. + +### `drive_accounts` + +**Properties** + - `id`: Primary Key. + - `code`: Identifer code of the account. + - `created_at`: Creation time of record. + - `deleted_at`: Deletion time of record. + +### `drive_enterprises` + +**Properties** + - `id`: Primary Key. + - `drive_account_id`: Belonged account's [drive_accounts.id](#drive_accounts) + - `name`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_enterprise_employees` + +**Properties** + - `id`: Primary Key. + - `drive_enterprise_id`: + - `drive_member_id`: + - `role`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_enterprise_employee_appointments` + +**Properties** + - `id`: Primary Key. + - `drive_enterprise_employee_id`: + - `drive_customer_id`: + - `role`: + - `created_at`: + +### `drive_enterprise_teams` + +**Properties** + - `id`: Primary Key. + - `drive_enterprise_id`: + - `name`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_enterprise_team_companions` + +**Properties** + - `id`: Primary Key. + - `drive_enterprise_team_id`: + - `drive_enterprise_employee_id`: + - `role`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_enterprise_team_companion_appointments` + +**Properties** + - `id`: Primary Key. + - `drive_enterprise_team_companion_id`: + - `drive_customer_id`: + - `role`: + - `created_at`: + + +## Repositories +```mermaid +erDiagram +"drive_repositories" { + String id PK + String drive_account_id FK + String drive_customer_id FK + String code + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_repository_buckets" { + String id PK + String drive_repository_id FK + String drive_repository_folder_id FK "nullable" + String drive_customer_id FK + String type + String name + String extension "nullable" + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_repository_folders" { + String id PK +} +"drive_repository_files" { + String id PK + String url +} +"drive_repository_shortcuts" { + String id PK + String drive_repository_bucket_id FK +} +"drive_repository_accesses" { + String id PK + String drive_repository_id FK + String drive_customer_id FK + String drive_account_id FK "nullable" + String drive_enterprise_team_id FK "nullable" + String role + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"drive_repository_tags" { + String id PK + String drive_repository_id FK + String value + Int sequence +} +"drive_repository_buckets" }o--|| "drive_repositories" : repository +"drive_repository_buckets" }o--|| "drive_repository_folders" : folder +"drive_repository_folders" |o--|| "drive_repository_buckets" : base +"drive_repository_files" |o--|| "drive_repository_buckets" : base +"drive_repository_shortcuts" |o--|| "drive_repository_buckets" : base +"drive_repository_shortcuts" }o--|| "drive_repository_buckets" : target +"drive_repository_accesses" }o--|| "drive_repositories" : repository +"drive_repository_tags" }o--|| "drive_repositories" : repository +``` + +### `drive_repositories` + +**Properties** + - `id`: Primary Key. + - `drive_account_id`: + - `drive_customer_id`: + - `code`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_repository_buckets` + +**Properties** + - `id`: Primary Key. + - `drive_repository_id`: + - `drive_repository_folder_id`: + - `drive_customer_id`: + - `type`: + - `name`: + - `extension`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_repository_folders` + +**Properties** + - `id`: PK + FK. + +### `drive_repository_files` + +**Properties** + - `id`: PK + FK. + - `url`: + +### `drive_repository_shortcuts` + +**Properties** + - `id`: PK + FK. + - `drive_repository_bucket_id`: + +### `drive_repository_accesses` + +**Properties** + - `id`: Primary Key. + - `drive_repository_id`: + - `drive_customer_id`: + - `drive_account_id`: + - `drive_enterprise_team_id`: + - `role`: + - `created_at`: + - `updated_at`: + - `deleted_at`: + +### `drive_repository_tags` + +**Properties** + - `id`: Primary Key. + - `drive_repository_id`: + - `value`: + - `sequence`: + + +## Issues +```mermaid +erDiagram +"drive_repository_issues" { + String id PK + String drive_repository_id FK + String drive_customer_id FK +} +"drive_repository_issue_comments" { + String id PK + String drive_customer_id FK +} +``` + +### `drive_repository_issues` + +**Properties** + - `id`: Primary Key. + - `drive_repository_id`: + - `drive_customer_id`: + +### `drive_repository_issue_comments` + +**Properties** + - `id`: Primary Key. + - `drive_customer_id`: \ No newline at end of file diff --git a/test/drive.prisma b/test/drive.prisma new file mode 100644 index 0000000..ea490bb --- /dev/null +++ b/test/drive.prisma @@ -0,0 +1,1074 @@ +datasource db { + provider = "postgresql" + url = env("POSTGRES_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["views"] +} + +generator markdown { + provider = "node ./lib/executable/markdown" + title = "Virtual Drive System" + output = "./drive.md" +} + +//----------------------------------------------------------- +// ARTICLES +//----------------------------------------------------------- +/// Attachment File. +/// +/// Every attachment files that are managed in this drive mall system. +/// +/// For reference, it is possible to omit one of file name or extension like +/// `.gitignore` or `README` case, but not possible to omit both of them, +/// +/// @namespace Articles +/// @author Samchon +model attachment_files { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// File name, except extension. + /// + /// Possible to omit like `.gitignore` case. + /// + /// @minLength 1 + /// @maxLength 255 + name String? @db.VarChar + + //----------------------------------------------------------- + // ARTICLES + //----------------------------------------------------------- + + /// Extension. + /// + /// Possible to omit like `README` case. + /// + /// @minLength 1 + /// @maxLength 8 + extension String? @db.VarChar + + /// URL path of the real file. + /// + /// @format url + url String @db.VarChar + + /// Creation time of record. + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + bbs_article_snapshot_files bbs_article_snapshot_files[] + bbs_article_comment_snapshots_files bbs_article_comment_snapshot_files[] +} + +/// Article entity. +/// +/// `bbs_articles` is a super-type entity of all kinds of articles in the +/// current drive mall system, literally shaping individual articles of +/// the bulletin board. +/// +/// And, as you can see, the elements that must inevitably exist in the +/// article, such as the title or the body, do not exist in the `bbs_articles`, +/// but exist in the subsidiary entity, {@link bbs_article_snapshots}, as a +/// 1: N relationship, which is because a new snapshot record is published +/// every time the article is modified. +/// +/// The reason why a new snapshot record is published every time the article +/// is modified is to preserve the evidence. Due to the nature of e-community, +/// there is always a threat of dispute among the participants. And it can +/// happen that disputes arise through articles or comments, and to prevent +/// such things as modifying existing articles to manipulate the situation, +/// the article is designed in this structure. +/// +/// In other words, to keep evidence, and prevent fraud. +/// +/// @namespace Articles +/// @author Samchon +model bbs_articles { + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Creation time of article. + created_at DateTime @db.Timestamptz + + /// Deletion time of article. + /// + /// To keep evidence, do not delete the article, but just mark it as + /// deleted. + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + /// List of snapshots. + /// + /// It is created for the first time when an article is created, and is + /// accumulated every time the article is modified. + /// + /// @minItems 1 + snapshots bbs_article_snapshots[] + + /// List of comments. + comments bbs_article_comments[] + drive_repository_issues drive_repository_issues[] + + @@index([created_at]) +} + +/// Snapshot of article. +/// +/// `bbs_article_snapshots` is a snapshot entity that contains the contents of +/// the article, as mentioned in {@link bbs_articles}, the contents of the +/// article are separated from the article record to keep evidence and prevent +/// fraud. +/// +/// @namespace Articles +/// @author Samchon +model bbs_article_snapshots { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belong article's {@link bbs_articles.id} + /// + /// @format uuid + bbs_article_id String @db.Uuid + + /// Format of body. + /// + /// Same meaning with extension like `html`, `md`, `txt`. + format String @db.VarChar + + /// Title of article. + title String @db.VarChar + + /// Content body of article. + body String + + /// Creation time of record. + /// + /// It means creation time or update time or article. + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + /// Belong article info. + article bbs_articles @relation(fields: [bbs_article_id], references: [id], onDelete: Cascade) + + /// List of wrappers of attachment files. + to_files bbs_article_snapshot_files[] + + @@index([bbs_article_id, created_at]) +} + +/// Attachment file of article snapshot. +/// +/// `bbs_article_snapshot_files` is an entity that shapes the attached files of +/// the article snapshot. +/// +/// `bbs_article_snapshot_files` is a typical pair relationship table to +/// resolve the M: N relationship between {@link bbs_article_snapshots} and +/// {@link attachment_files} tables. Also, to ensure the order of the attached +/// files, it has an additional `sequence` attribute, which we will continue to +/// see in this documents. +/// +/// @namespace Articles +/// @author Samchon +model bbs_article_snapshot_files { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged snapshot's {@link bbs_article_snapshots.id} + /// + /// @format uuid + bbs_article_snapshot_id String @db.Uuid + + /// Belonged file's {@link attachment_files.id} + /// + /// @format uuid + attachment_file_id String @db.Uuid + + /// Sequence of attachment file in the snapshot. + /// + /// @format int + sequence Int @db.Integer + + //---- + // RELATIONS + //---- + /// Belonged article. + snapshot bbs_article_snapshots @relation(fields: [bbs_article_snapshot_id], references: [id], onDelete: Cascade) + + /// Belonged file. + file attachment_files @relation(fields: [attachment_file_id], references: [id], onDelete: Cascade) + + @@index([bbs_article_snapshot_id]) + @@index([attachment_file_id]) +} + +/// Comment written on an article. +/// +/// `bbs_article_comments` is an entity that shapes the comments written on an +/// article. +/// +/// And for this comment, as in the previous relationship between +/// {@link bbs_articles} and {@link bbs_article_snapshots}, the content body +/// of the comment is stored in the sub {@link bbs_article_comment_snapshots} +/// table for evidentialism, and a new snapshot record is issued every time +/// the comment is modified. +/// +/// Also, `bbs_article_comments} is expressing the relationship of the +/// hierarchical reply structure through the `parent_id` attribute. +/// +/// @namespace Articles +/// @author Samchon +model bbs_article_comments { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged article's {@link bbs_articles.id} + /// + /// @format uuid + bbs_article_id String @db.Uuid + + /// Parent comment's {@link bbs_article_comments.id} + /// + /// Used to express the hierarchical reply structure. + /// + /// @format uuid + parent_id String? @db.Uuid + + /// Creation time of comment. + created_at DateTime @db.Timestamptz + + /// Deletion time of comment. + /// + /// Do not allow to delete the comment, but just mark it as deleted, + /// to keep evidence. + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + /// Belonged article. + article bbs_articles @relation(fields: [bbs_article_id], references: [id], onDelete: Cascade) + + /// Parent comment. + /// + /// Only when reply case. + parent bbs_article_comments? @relation("bbs_article_comments_reply", fields: [parent_id], references: [id], onDelete: Cascade) + + /// List of children comments. + /// + /// Reply comments of current. + children bbs_article_comments[] @relation("bbs_article_comments_reply") + + /// List of snapshots. + /// + /// It is created for the first time when a comment is created, and is + /// accumulated every time the comment is modified. + /// + /// @minItems 1 + snapshots bbs_article_comment_snapshots[] + drive_repository_issue_comments drive_repository_issue_comments[] + + @@index([bbs_article_id, parent_id, created_at]) +} + +/// Snapshot of comment. +/// +/// `bbs_article_comment_snapshots` is a snapshot entity that contains the +/// contents of the comment. +/// +/// As mentioned in {@link bbs_article_comments}, designed to keep evidence +/// and prevent fraud. +/// +/// @namespace Articles +/// @author Samchon +model bbs_article_comment_snapshots { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged article's {@link bbs_article_comments.id} + /// + /// @format uuid + bbs_article_comment_id String @db.Uuid + + /// Format of content body. + /// + /// Same meaning with extension like `html`, `md`, `txt`. + format String @db.VarChar + + /// Content body of comment. + body String + + /// Creation time of record. + /// + /// It means creation time or update time or comment. + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + /// Belong comment info. + comment bbs_article_comments @relation(fields: [bbs_article_comment_id], references: [id], onDelete: Cascade) + + /// List of wrappers of attachment files. + files bbs_article_comment_snapshot_files[] + + @@index([bbs_article_comment_id, created_at]) +} + +/// Attachment file of comment snapshot. +/// +/// `bbs_article_comment_snapshot_files` is an entity resolving the M:N +/// relationship between {@link bbs_article_comment_snapshots} and +/// {@link attachment_files} tables. +/// +/// @namespace Articles +/// @author Samchon +model bbs_article_comment_snapshot_files { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged snapshot's {@link bbs_article_comment_snapshots.id} + /// + /// @format uuid + bbs_article_comment_snapshot_id String @db.Uuid + + /// Belonged file's {@link attachment_files.id} + /// + /// @format uuid + attachment_file_id String @db.Uuid + + /// Sequence order. + /// + /// Sequence order of the attached file in the belonged snapshot. + /// + /// @type int + sequence Int @db.Integer + + //---- + // RELATIONS + //---- + /// Belonged article. + snapshot bbs_article_comment_snapshots @relation(fields: [bbs_article_comment_snapshot_id], references: [id], onDelete: Cascade) + + /// Belonged file. + file attachment_files @relation(fields: [attachment_file_id], references: [id], onDelete: Cascade) + + @@index([bbs_article_comment_snapshot_id]) + @@index([attachment_file_id]) +} + +//----------------------------------------------------------- +// ACTORS +//----------------------------------------------------------- +/// @namespace Actors +/// @author Samchon +model drive_customers { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged member's {@link drive_members.id} + /// + /// @format uuid + drive_member_id String? @db.Uuid /// @format uuid + + /// Belonged external service user's {@link drive_external_users.id} + /// + /// @format uuid + drive_external_user_id String? @db.Uuid /// @format uuid + + /// Belonged citizen's {@link drive_citizens.id} + /// + /// @format uuid + drive_citizen_id String? @db.Uuid /// @format uuid + + /// Connection URL. + /// + /// {@link window.location.href} + /// + /// @format url + href String @db.VarChar /// @format url + + /// Referrer URL. + /// + /// {@link window.document.referrer} + /// + /// @format url + referrer String @db.VarChar /// @format url + + /// IP address, + /// + /// @pattern ((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$)) + ip String @db.VarChar + + /// Creation time of record. + /// + /// It means the time when the customer connected to the drive mall. + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + member drive_members? @relation(fields: [drive_member_id], references: [id], onDelete: Cascade) + external_user drive_external_users? @relation(fields: [drive_external_user_id], references: [id], onDelete: Cascade) + citizen drive_citizens? @relation(fields: [drive_citizen_id], references: [id], onDelete: Cascade) + drive_enterprise_employee_appointments drive_enterprise_employee_appointments[] + drive_enterprise_team_companion_appointments drive_enterprise_team_companion_appointments[] + drive_repositories drive_repositories[] + drive_repository_buckets drive_repository_buckets[] + drive_repository_accesses drive_repository_accesses[] + drive_repository_issues drive_repository_issues[] + drive_repository_issue_comments drive_repository_issue_comments[] + + @@index([drive_member_id]) + @@index([drive_external_user_id]) + @@index([drive_citizen_id]) +} + +/// @namespace Actors +/// @author Samchon +model drive_external_users { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Identifier code of the external service. + /// + /// It can be same with {@link drive_channels.code} in common. + application String @db.VarChar + + /// Identifier key of external user from the external system. + uid String @db.VarChar + + /// Nickname of external user in the external system. + nickname String @db.VarChar + + /// Additional information about external user from the external system. + data String? + + /// Password of external user from the external system. + /// + /// This is a password issued to the user by an external service, and is + /// by no means the actual user password. However, for customers who + /// entered the same application and code as the current external system + /// user, this is to determine whether to view this as a correct external + /// system user or a violation. + password String @db.VarChar + + /// Creation time of record. + /// + /// Another word, first time when the external user connected. + created_at DateTime + + //---- + // RELATIONS + //---- + drive_customers drive_customers[] +} + +/// @namespace Actors +/// @author Samchon +model drive_citizens { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Mobile phone number. + /// + /// @pattern ^[0-9]*$ + mobile String @db.VarChar + + /// Real name, or equivalent name identifiable. + name String @db.VarChar + + /// Creation time of record. + /// + /// In other words, the 1st time of citizen activation. + created_at DateTime @db.Timestamptz + + /// Deletion time of record. + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + drive_customers drive_customers[] + drive_members drive_members[] +} + +/// @namespace Actors +/// @author Samchon +model drive_members { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged account's {@link drive_accounts.id} + /// + /// @format uuid + drive_account_id String? @db.Uuid + + /// Belonged citizen's {@link drive_citizens.id} + /// + /// @format uuid + drive_citizen_id String? @db.Uuid + + /// Nickname. + nickname String @db.VarChar + + /// Password for log-in. + password String @db.VarChar + + /// Creation time of record. + /// + /// In other words, the joining time. + created_at DateTime @db.Timestamptz + + /// Update time of record. + updated_at DateTime @db.Timestamptz + + /// Deletion time of record. + withdrawn_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + account drive_accounts? @relation(fields: [drive_account_id], references: [id], onDelete: Cascade) + citizen drive_citizens? @relation(fields: [drive_citizen_id], references: [id], onDelete: Cascade) + drive_customers drive_customers[] + drive_member_emails drive_member_emails[] + drive_enterprise_employees drive_enterprise_employees[] +} + +/// @namespace Actors +/// @author Samchon +model drive_member_emails { + //---- + // COLUMNS + //---- + /// @format uuid + id String @id @db.Uuid + + /// Belonged member's {@link drive_members.id} + /// + /// @format uuid + drive_member_id String @db.Uuid + + /// Email address. + /// + /// @format email + value String @db.VarChar + + /// Creation time of record. + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + member drive_members @relation(fields: [drive_member_id], references: [id], onDelete: Cascade) +} + +/// @namespace Actors +/// @author Samchon +model drive_accounts { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Identifer code of the account. + code String @db.VarChar + + /// Creation time of record. + created_at DateTime @db.Timestamptz + + /// Deletion time of record. + deleted_at DateTime? @db.Timestamptz + drive_members drive_members[] + drive_enterprises drive_enterprises[] + drive_repositories drive_repositories[] + drive_repository_accesses drive_repository_accesses[] + + //---- + // RELATIONS + //---- + @@unique([code]) + @@index([created_at]) +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprises { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + /// Belonged account's {@link drive_accounts.id} + /// + /// @format uuid + drive_account_id String @db.Uuid + + name String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + account drive_accounts @relation(fields: [drive_account_id], references: [id], onDelete: Cascade) + drive_enterprise_employees drive_enterprise_employees[] + drive_enterprise_teams drive_enterprise_teams[] +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprise_employees { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_enterprise_id String @db.Uuid + + drive_member_id String @db.Uuid + + role String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + enterprise drive_enterprises @relation(fields: [drive_enterprise_id], references: [id], onDelete: Cascade) + member drive_members @relation(fields: [drive_member_id], references: [id], onDelete: Cascade) + drive_enterprise_employee_appointments drive_enterprise_employee_appointments[] + drive_enterprise_team_companions drive_enterprise_team_companions[] +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprise_employee_appointments { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_enterprise_employee_id String @db.Uuid + + drive_customer_id String @db.Uuid + + role String @db.VarChar + + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + employee drive_enterprise_employees @relation(fields: [drive_enterprise_employee_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprise_teams { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_enterprise_id String @db.Uuid + + name String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + enterprise drive_enterprises @relation(fields: [drive_enterprise_id], references: [id], onDelete: Cascade) + drive_enterprise_team_companions drive_enterprise_team_companions[] + drive_repository_accesses drive_repository_accesses[] +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprise_team_companions { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_enterprise_team_id String @db.Uuid + + drive_enterprise_employee_id String @db.Uuid + + role String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + team drive_enterprise_teams @relation(fields: [drive_enterprise_team_id], references: [id], onDelete: Cascade) + employee drive_enterprise_employees @relation(fields: [drive_enterprise_employee_id], references: [id], onDelete: Cascade) + drive_enterprise_team_companion_appointments drive_enterprise_team_companion_appointments[] +} + +/// @namespace Actors +/// @author Samchon +model drive_enterprise_team_companion_appointments { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_enterprise_team_companion_id String @db.Uuid + + drive_customer_id String @db.Uuid + + role String @db.VarChar + + created_at DateTime @db.Timestamptz + + //---- + // RELATIONS + //---- + companion drive_enterprise_team_companions @relation(fields: [drive_enterprise_team_companion_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) +} + +//----------------------------------------------------------- +// REPOSITORY +//----------------------------------------------------------- +/// @namespace Repositories +/// @author Samchon +model drive_repositories { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_account_id String @db.Uuid + + drive_customer_id String @db.Uuid + + code String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + account drive_accounts @relation(fields: [drive_account_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) + drive_repository_buckets drive_repository_buckets[] + drive_repository_accesses drive_repository_accesses[] + drive_repository_tags drive_repository_tags[] + drive_repository_issues drive_repository_issues[] +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_buckets { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_repository_id String @db.Uuid + + drive_repository_folder_id String? @db.Uuid + + drive_customer_id String @db.Uuid + + type String @db.VarChar + + name String @db.VarChar(255) + + extension String? @db.VarChar(8) + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + repository drive_repositories @relation(fields: [drive_repository_id], references: [id], onDelete: Cascade) + folder drive_repository_folders? @relation("rel_drive_repository_bucket_parent", fields: [drive_repository_folder_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) + + of_folder drive_repository_folders? @relation("rel_drive_repository_folders_base") + of_file drive_repository_files? + of_shortcut drive_repository_shortcuts? @relation("rel_drive_repository_shortcuts_base") + + shortcuts drive_repository_shortcuts[] @relation("rel_drive_repository_shortcuts_target") +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_folders { + //---- + // COLUMNS + //---- + /// PK + FK. + /// + /// @format uuid + id String @id @db.Uuid + + //---- + // RELATIONS + //---- + base drive_repository_buckets @relation("rel_drive_repository_folders_base", fields: [id], references: [id], onDelete: Cascade) + children drive_repository_buckets[] @relation("rel_drive_repository_bucket_parent") +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_files { + //---- + // COLUMNS + //---- + /// PK + FK. + /// + /// @format uuid + id String @id @db.Uuid + + url String @db.VarChar + + //---- + // RELATIONS + //---- + base drive_repository_buckets @relation(fields: [id], references: [id], onDelete: Cascade) +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_shortcuts { + //---- + // COLUMNS + //---- + /// PK + FK. + /// + /// @format uuid + id String @id @db.Uuid + + drive_repository_bucket_id String @db.Uuid + + //---- + // RELATIONS + //---- + base drive_repository_buckets @relation("rel_drive_repository_shortcuts_base", fields: [id], references: [id], onDelete: Cascade) + target drive_repository_buckets @relation("rel_drive_repository_shortcuts_target", fields: [drive_repository_bucket_id], references: [id], onDelete: Cascade) +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_accesses { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_repository_id String @db.Uuid + + drive_customer_id String @db.Uuid + + drive_account_id String? @db.Uuid + + drive_enterprise_team_id String? @db.Uuid + + role String @db.VarChar + + created_at DateTime @db.Timestamptz + + updated_at DateTime @db.Timestamptz + + deleted_at DateTime? @db.Timestamptz + + //---- + // RELATIONS + //---- + repository drive_repositories @relation(fields: [drive_repository_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) + account drive_accounts? @relation(fields: [drive_account_id], references: [id], onDelete: Cascade) + team drive_enterprise_teams? @relation(fields: [drive_enterprise_team_id], references: [id], onDelete: Cascade) +} + +/// @namespace Repositories +/// @author Samchon +model drive_repository_tags { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_repository_id String @db.Uuid + + value String @db.VarChar + + sequence Int @db.Integer + + //---- + // RELATIONS + //---- + repository drive_repositories @relation(fields: [drive_repository_id], references: [id], onDelete: Cascade) +} + +//----------------------------------------------------------- +// ISSUES +//----------------------------------------------------------- +/// @namespace Issues +/// @author Samchon +model drive_repository_issues { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_repository_id String @db.Uuid + + drive_customer_id String @db.Uuid + + //---- + // RELATIONS + //---- + base bbs_articles @relation(fields: [id], references: [id], onDelete: Cascade) + repository drive_repositories @relation(fields: [drive_repository_id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) +} + +/// @namespace Issues +/// @author Samchon +model drive_repository_issue_comments { + //---- + // COLUMNS + //---- + /// Primary Key. + /// + /// @format uuid + id String @id @db.Uuid + + drive_customer_id String @db.Uuid + + //---- + // RELATIONS + //---- + base bbs_article_comments @relation(fields: [id], references: [id], onDelete: Cascade) + customer drive_customers @relation(fields: [drive_customer_id], references: [id], onDelete: Cascade) +} diff --git a/test/implicit.md b/test/implicit.md index 495ff3e..3c9185b 100644 --- a/test/implicit.md +++ b/test/implicit.md @@ -49,13 +49,13 @@ erDiagram String B FK } "bbs_article_snapshots" }|--|| "bbs_articles" : article -"bbs_article_comments" }|--|| "bbs_articles" : article +"bbs_article_comments" }o--|| "bbs_articles" : article "bbs_article_comments" }o--o| "bbs_article_comments" : parent "bbs_article_comment_snapshots" }|--|| "bbs_article_comments" : comment -"_attachment_filesTobbs_article_snapshots" }|--|| "attachment_files" : attachment_files -"_attachment_filesTobbs_article_snapshots" }|--|| "bbs_article_snapshots" : bbs_article_snapshots -"_attachment_filesTobbs_article_comment_snapshots" }|--|| "attachment_files" : attachment_files -"_attachment_filesTobbs_article_comment_snapshots" }|--|| "bbs_article_comment_snapshots" : bbs_article_comment_snapshots +"_attachment_filesTobbs_article_snapshots" }o--|| "attachment_files" : attachment_files +"_attachment_filesTobbs_article_snapshots" }o--|| "bbs_article_snapshots" : bbs_article_snapshots +"_attachment_filesTobbs_article_comment_snapshots" }o--|| "attachment_files" : attachment_files +"_attachment_filesTobbs_article_comment_snapshots" }o--|| "bbs_article_comment_snapshots" : bbs_article_comment_snapshots ``` ### `bbs_articles` diff --git a/test/summary.md b/test/summary.md index 32090d7..db1d5c2 100644 --- a/test/summary.md +++ b/test/summary.md @@ -52,13 +52,13 @@ erDiagram Int sequence } "bbs_article_snapshots" }|--|| "bbs_articles" : article -"bbs_article_snapshot_files" }|--|| "bbs_article_snapshots" : snapshot -"bbs_article_snapshot_files" }|--|| "attachment_files" : file -"bbs_article_comments" }|--|| "bbs_articles" : article +"bbs_article_snapshot_files" }o--|| "bbs_article_snapshots" : snapshot +"bbs_article_snapshot_files" }o--|| "attachment_files" : file +"bbs_article_comments" }o--|| "bbs_articles" : article "bbs_article_comments" }o--o| "bbs_article_comments" : parent "bbs_article_comment_snapshots" }|--|| "bbs_article_comments" : comment -"bbs_article_comment_snapshot_files" }|--|| "bbs_article_comment_snapshots" : snapshot -"bbs_article_comment_snapshot_files" }|--|| "attachment_files" : file +"bbs_article_comment_snapshot_files" }o--|| "bbs_article_comment_snapshots" : snapshot +"bbs_article_comment_snapshot_files" }o--|| "attachment_files" : file ``` ### `attachment_files`