0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

Init table type generation

This commit is contained in:
Gao Sun 2021-06-23 00:07:51 +08:00
parent b775168a7b
commit 17b6f98833
8 changed files with 312 additions and 2 deletions

View file

@ -1,4 +1,7 @@
{
"space": true,
"prettier": true
"prettier": true,
"rules": {
"unicorn/no-array-reduce": "off"
}
}

View file

@ -6,7 +6,8 @@
"author": "Logto",
"license": "UNLICENSED",
"scripts": {
"build": "tsc",
"generate": "ts-node src/gen/index.ts",
"build": "rm -rf lib/ && tsc --p tsconfig.build.json",
"lint": "xo src/"
},
"engines": {
@ -14,6 +15,8 @@
"yarn": "1"
},
"devDependencies": {
"@types/node": "14",
"ts-node": "^10.0.0",
"typescript": "^4.3.4",
"xo": "0.39.1"
}

View file

@ -0,0 +1,67 @@
import assert from 'assert';
import fs from 'fs/promises';
import path from 'path';
import { findFirstParentheses, getType, normalizeWhitespaces, removeParentheses } from './utils';
type Field = {
name: string;
type: string;
required: boolean;
isArray: boolean;
};
type Table = {
name: string;
fields: Field[];
};
const dir = 'tables';
const generate = async () => {
const files = await fs.readdir(dir);
const generated = await Promise.all(
files.map(async (file) => {
return (await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }))
.split(';')
.map((value) => normalizeWhitespaces(value).toLowerCase())
.filter((value) => value.startsWith('create table'))
.map((value) => findFirstParentheses(value))
.filter((value): value is NonNullable<typeof value> => Boolean(value))
.map<Table>(({ prefix, body }) => {
const name = normalizeWhitespaces(prefix).split(' ')[2];
assert(name, 'Missing table name: ' + prefix);
const fields = removeParentheses(body)
.split(',')
.map((value) => normalizeWhitespaces(value))
.filter((value) =>
['primary', 'foreign', 'unique', 'exclude', 'check'].every(
(constraint) => !value.startsWith(constraint)
)
)
.map<Field>((value) => {
const [name, type, ...rest] = value.split(' ');
assert(name && type, 'Missing column name or type: ' + value);
const restJoined = rest.join(' ');
// CAUTION: Only works for single dimension arrays
const isArray = Boolean(/\[.*]/.test(type)) || restJoined.includes('array');
const required = restJoined.includes('not null');
return {
name,
type: getType(type),
isArray,
required,
};
});
return { name, fields };
});
})
);
const tables = generated.flat();
console.log(tables);
};
void generate();

View file

@ -0,0 +1,120 @@
import { Optional } from '../global';
export const normalizeWhitespaces = (string: string): string => string.replace(/\s+/g, ' ').trim();
const getCountDelta = (value: string): number => {
if (value === '(') {
return 1;
}
if (value === ')') {
return -1;
}
return 0;
};
export const removeParentheses = (value: string) =>
Object.values(value).reduce<{ result: string; count: number }>(
(previous, current) => {
const count = previous.count + getCountDelta(current);
return count === 0 && current !== ')'
? { result: previous.result + current, count }
: { result: previous.result, count };
},
{
result: '',
count: 0,
}
).result;
type ParenthesesMatch = { body: string; prefix: string };
export const findFirstParentheses = (value: string): Optional<ParenthesesMatch> => {
const { matched, count, ...rest } = Object.values(value).reduce<{
body: string;
prefix: string;
count: number;
matched: boolean;
}>(
(previous, current) => {
const count = previous.count + getCountDelta(current);
if (count === 0) {
if (current === ')') {
return {
...previous,
count,
matched: true,
};
}
return {
...previous,
count,
prefix: previous.prefix + current,
};
}
return {
...previous,
count,
body: previous.body + (count === 1 && current === '(' ? '' : current),
};
},
{
body: '',
prefix: '',
count: 0,
matched: false,
}
);
return matched ? rest : undefined;
};
const getRawType = (value: string): string => {
const bracketIndex = value.indexOf('[');
return bracketIndex === -1 ? value : value.slice(0, bracketIndex);
};
// Reference: https://github.com/SweetIQ/schemats/blob/7c3d3e16b5d507b4d9bd246794e7463b05d20e75/src/schemaPostgres.ts
// eslint-disable-next-line complexity
export const getType = (value: string): 'string' | 'number' | 'boolean' | 'Object' | 'Date' => {
switch (getRawType(value)) {
case 'bpchar':
case 'char':
case 'varchar':
case 'text':
case 'citext':
case 'uuid':
case 'bytea':
case 'inet':
case 'time':
case 'timetz':
case 'interval':
case 'name':
return 'string';
case 'int2':
case 'int4':
case 'int8':
case 'bigint':
case 'float4':
case 'float8':
case 'numeric':
case 'money':
case 'oid':
return 'number';
case 'bool':
return 'boolean';
case 'json':
case 'jsonb':
return 'Object';
case 'date':
case 'timestamp':
case 'timestamptz':
return 'Date';
default:
throw new Error('Unable to parse type: ' + value);
}
};

View file

@ -0,0 +1 @@
export type Optional<T> = T | undefined;

View file

@ -0,0 +1,28 @@
create table oidc_model_instances (
model_name varchar(64) not null,
id varchar(128) not null,
payload jsonb not null,
expires_at bigint not null,
user_code varchar(128),
uid varchar(128),
grant_id varchar(128),
primary key (model_name, id)
);
create index oidc_model_instances__model_name_user_code
on oidc_model_instances (
model_name,
user_code
);
create index oidc_model_instances__model_name_uid
on oidc_model_instances (
model_name,
uid
);
create index oidc_model_instances__model_name_grant_id
on oidc_model_instances (
model_name,
grant_id
);

View file

@ -0,0 +1,4 @@
{
"extends": "./tsconfig",
"exclude": ["src/gen"]
}

View file

@ -278,6 +278,26 @@
dependencies:
defer-to-connect "^1.0.1"
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
"@tsconfig/node16@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
"@types/eslint@^7.2.13":
version "7.2.13"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.13.tgz#e0ca7219ba5ded402062ad6f926d491ebb29dd53"
@ -324,6 +344,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26"
integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==
"@types/node@14":
version "14.17.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d"
integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
@ -476,6 +501,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -654,6 +684,11 @@ buf-compare@^1.0.0:
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
integrity sha1-/vKNqLgROgoNtEMLC2Rntpcws0o=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@ -880,6 +915,11 @@ cosmiconfig@^7.0.0:
path-type "^4.0.0"
yaml "^1.10.0"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@ -991,6 +1031,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dir-glob@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
@ -2526,6 +2571,11 @@ make-dir@^3.0.0, make-dir@^3.0.2:
dependencies:
semver "^6.0.0"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
map-cache@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@ -3400,6 +3450,14 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-support@^0.5.17:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
@ -3410,6 +3468,11 @@ source-map@^0.5.0, source-map@^0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@ -3630,6 +3693,22 @@ trim-newlines@^3.0.0:
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
ts-node@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be"
integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==
dependencies:
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.17"
yn "3.1.1"
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@ -3926,6 +4005,11 @@ yargs-parser@^20.2.3:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"