2023-09-19 20:49:28 +08:00
|
|
|
import { Validator } from "jsonschema";
|
2023-09-19 20:49:31 +08:00
|
|
|
import { ddbSchema, jsonSchema } from "../schemas";
|
2023-09-19 20:49:28 +08:00
|
|
|
|
2023-09-19 20:50:39 +08:00
|
|
|
function enterFullscreen() {
|
2023-09-19 20:48:35 +08:00
|
|
|
const element = document.documentElement;
|
|
|
|
if (element.requestFullscreen) {
|
|
|
|
element.requestFullscreen();
|
|
|
|
} else if (element.mozRequestFullScreen) {
|
|
|
|
element.mozRequestFullScreen();
|
|
|
|
} else if (element.webkitRequestFullscreen) {
|
|
|
|
element.webkitRequestFullscreen();
|
|
|
|
} else if (element.msRequestFullscreen) {
|
|
|
|
element.msRequestFullscreen();
|
|
|
|
}
|
2023-09-19 20:50:39 +08:00
|
|
|
}
|
2023-09-19 20:48:35 +08:00
|
|
|
|
2023-09-19 20:50:39 +08:00
|
|
|
function exitFullscreen() {
|
2023-09-19 20:48:35 +08:00
|
|
|
if (document.exitFullscreen) {
|
|
|
|
document.exitFullscreen();
|
|
|
|
} else if (document.mozCancelFullScreen) {
|
|
|
|
document.mozCancelFullScreen();
|
|
|
|
} else if (document.webkitExitFullscreen) {
|
|
|
|
document.webkitExitFullscreen();
|
|
|
|
} else if (document.msExitFullscreen) {
|
|
|
|
document.msExitFullscreen();
|
|
|
|
}
|
2023-09-19 20:50:39 +08:00
|
|
|
}
|
2023-09-19 20:48:35 +08:00
|
|
|
|
2023-09-19 20:50:39 +08:00
|
|
|
function jsonDiagramIsValid(obj) {
|
2023-09-19 20:49:28 +08:00
|
|
|
return new Validator().validate(obj, jsonSchema).valid;
|
2023-09-19 20:50:39 +08:00
|
|
|
}
|
2023-09-19 20:49:28 +08:00
|
|
|
|
2023-09-19 20:50:39 +08:00
|
|
|
function ddbDiagramIsValid(obj) {
|
2023-09-19 20:49:31 +08:00
|
|
|
return new Validator().validate(obj, ddbSchema).valid;
|
2023-09-19 20:50:39 +08:00
|
|
|
}
|
2023-09-19 20:49:31 +08:00
|
|
|
|
2023-09-19 20:49:39 +08:00
|
|
|
function dataURItoBlob(dataUrl) {
|
|
|
|
const byteString = atob(dataUrl.split(",")[1]);
|
|
|
|
const mimeString = dataUrl.split(",")[0].split(":")[1].split(";")[0];
|
|
|
|
const arrayBuffer = new ArrayBuffer(byteString.length);
|
|
|
|
const intArray = new Uint8Array(arrayBuffer);
|
|
|
|
|
|
|
|
for (let i = 0; i < byteString.length; i++) {
|
|
|
|
intArray[i] = byteString.charCodeAt(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Blob([intArray], { type: mimeString });
|
|
|
|
}
|
|
|
|
|
2023-09-19 20:50:39 +08:00
|
|
|
function jsonToSQL(obj) {
|
2023-09-19 20:50:46 +08:00
|
|
|
return `${obj.tables
|
2023-09-19 20:50:39 +08:00
|
|
|
.map(
|
|
|
|
(table) =>
|
|
|
|
`${
|
|
|
|
table.comment === "" ? "" : `/* ${table.comment} */\n`
|
|
|
|
}CREATE TABLE \`${table.name}\` (\n${table.fields
|
|
|
|
.map(
|
|
|
|
(field) =>
|
|
|
|
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t\`${
|
|
|
|
field.name
|
2023-09-19 20:50:42 +08:00
|
|
|
}\` ${field.type}${
|
2023-09-19 20:50:45 +08:00
|
|
|
field.length !== ""
|
2023-09-19 20:50:43 +08:00
|
|
|
? `(${field.length})`
|
2023-09-19 20:50:44 +08:00
|
|
|
: field.type === "ENUM" || field.type === "SET"
|
|
|
|
? `(${field.values.map((v) => `"${v}"`).join(", ")})`
|
2023-09-19 20:50:43 +08:00
|
|
|
: ""
|
2023-09-19 20:50:42 +08:00
|
|
|
}${field.notNull ? " NOT NULL" : ""}${
|
|
|
|
field.increment ? " AUTO_INCREMENT" : ""
|
|
|
|
}${field.unique ? " UNIQUE" : ""}${
|
2023-09-19 20:50:45 +08:00
|
|
|
field.default !== ""
|
|
|
|
? ` DEFAULT ${
|
|
|
|
(field.type === "VARCHAR" || field.type === "ENUM") &&
|
|
|
|
field.default.toLowerCase() !== "null"
|
|
|
|
? `"${field.default}"`
|
|
|
|
: `${field.default}`
|
|
|
|
}`
|
|
|
|
: ""
|
|
|
|
}${field.check === "" ? "" : ` CHECK (${field.check})`}`
|
2023-09-19 20:50:39 +08:00
|
|
|
)
|
2023-09-19 20:50:45 +08:00
|
|
|
.join(",\n")}${
|
|
|
|
table.fields.filter((f) => f.primary).length > 0
|
|
|
|
? `,\n\tPRIMARY KEY(${table.fields
|
|
|
|
.filter((f) => f.primary)
|
|
|
|
.map((f) => `\`${f.name}\``)
|
|
|
|
.join(", ")})`
|
|
|
|
: ""
|
|
|
|
}\n);\n${
|
|
|
|
table.indices.length > 0
|
2023-09-19 20:50:46 +08:00
|
|
|
? `\n${table.indices.map(
|
2023-09-19 20:50:45 +08:00
|
|
|
(i) =>
|
|
|
|
`\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${
|
|
|
|
i.name
|
|
|
|
}\`\nON \`${table.name}\` (${i.fields
|
|
|
|
.map((f) => `\`${f}\``)
|
|
|
|
.join(", ")});`
|
|
|
|
)}`
|
|
|
|
: ""
|
|
|
|
}`
|
2023-09-19 20:50:39 +08:00
|
|
|
)
|
2023-09-19 20:50:46 +08:00
|
|
|
.join("\n")}\n${obj.references
|
|
|
|
.map(
|
|
|
|
(r) =>
|
|
|
|
`ALTER TABLE \`${
|
|
|
|
obj.tables[r.startTableId].name
|
|
|
|
}\`\nADD FOREIGN KEY(\`${
|
|
|
|
obj.tables[r.startTableId].fields[r.startFieldId].name
|
|
|
|
}\`) REFERENCES \`${obj.tables[r.endTableId].name}\`(\`${
|
|
|
|
obj.tables[r.endTableId].fields[r.endFieldId].name
|
|
|
|
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`
|
|
|
|
)
|
|
|
|
.join("\n")}`;
|
2023-09-19 20:50:39 +08:00
|
|
|
}
|
|
|
|
|
2023-09-19 20:51:14 +08:00
|
|
|
function arrayIsEqual(arr1, arr2) {
|
|
|
|
return JSON.stringify(arr1) === JSON.stringify(arr2);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateDiagram(diagram) {
|
|
|
|
const issues = [];
|
|
|
|
const duplicateTableNames = {};
|
|
|
|
|
|
|
|
diagram.tables.forEach((table) => {
|
2023-09-19 20:51:16 +08:00
|
|
|
if (table.name === "") {
|
|
|
|
issues.push(`Declared a table with no name`);
|
|
|
|
}
|
|
|
|
|
2023-09-19 20:51:14 +08:00
|
|
|
if (duplicateTableNames[table.name]) {
|
2023-09-19 20:51:16 +08:00
|
|
|
issues.push(`Duplicate table by the name "${table.name}"`);
|
2023-09-19 20:51:14 +08:00
|
|
|
} else {
|
|
|
|
duplicateTableNames[table.name] = true;
|
|
|
|
}
|
2023-09-19 20:51:16 +08:00
|
|
|
|
|
|
|
const duplicateFieldNames = {};
|
|
|
|
let hasPrimaryKey = false;
|
|
|
|
|
|
|
|
table.fields.forEach((field) => {
|
|
|
|
if (field.primary) {
|
|
|
|
hasPrimaryKey = true;
|
|
|
|
}
|
|
|
|
if (field.name === "") {
|
|
|
|
issues.push(`Empty field name in table "${table.name}"`);
|
|
|
|
}
|
|
|
|
if (field.type === "") {
|
|
|
|
issues.push(`Empty field type in table "${table.name}"`);
|
|
|
|
}
|
|
|
|
if (duplicateFieldNames[field.name]) {
|
|
|
|
issues.push(`Duplicate table fields in table "${table.name}"`);
|
|
|
|
} else {
|
|
|
|
duplicateFieldNames[field.name] = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const duplicateIndices = {};
|
|
|
|
table.indices.forEach((index) => {
|
|
|
|
if (duplicateIndices[index.name]) {
|
|
|
|
issues.push(`Duplicate index by the name "${index.name}"`);
|
|
|
|
} else {
|
|
|
|
duplicateIndices[index.name] = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
table.indices.forEach((index) => {
|
|
|
|
if (index.fields.length === 0) {
|
|
|
|
issues.push(`Empty index type in table "${table.name}"`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!hasPrimaryKey) {
|
|
|
|
issues.push(`Table "${table.name}" has no primary key`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const duplicateFKName = {};
|
|
|
|
diagram.relationships.forEach((relationship) => {
|
|
|
|
if (duplicateFKName[relationship.name]) {
|
|
|
|
issues.push(`Duplicate relationship by the name "${relationship.name}"`);
|
|
|
|
} else {
|
|
|
|
duplicateFKName[relationship.name] = true;
|
|
|
|
}
|
2023-09-19 20:51:14 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
const visitedTables = new Set();
|
|
|
|
|
|
|
|
function checkCircularRelationships(tableId, visited = []) {
|
|
|
|
if (visited.includes(tableId)) {
|
|
|
|
issues.push(
|
|
|
|
`Circular relationship involving table: "${diagram.tables[tableId].name}"`
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
visited.push(tableId);
|
|
|
|
visitedTables.add(tableId);
|
|
|
|
|
|
|
|
diagram.relationships.forEach((relationship) => {
|
|
|
|
if (relationship.startTableId === tableId) {
|
|
|
|
checkCircularRelationships(relationship.endTableId, [...visited]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
diagram.tables.forEach((table) => {
|
|
|
|
if (!visitedTables.has(table.id)) {
|
|
|
|
checkCircularRelationships(table.id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return issues;
|
|
|
|
}
|
|
|
|
|
2023-09-19 20:49:31 +08:00
|
|
|
export {
|
|
|
|
enterFullscreen,
|
|
|
|
exitFullscreen,
|
|
|
|
jsonDiagramIsValid,
|
|
|
|
ddbDiagramIsValid,
|
2023-09-19 20:49:39 +08:00
|
|
|
dataURItoBlob,
|
2023-09-19 20:50:39 +08:00
|
|
|
jsonToSQL,
|
2023-09-19 20:51:14 +08:00
|
|
|
validateDiagram,
|
|
|
|
arrayIsEqual,
|
2023-09-19 20:49:31 +08:00
|
|
|
};
|